Why does make_pair fail when template arguments are explicited? [duplicate] - c++

I was just playing around with g++ 4.7 (one of the later snapshots) with -std=c++11 enabled. I tried to compile some of my existing code base and one case that failed somewhat confuses me.
I would appreciate if someone can explain what is going on.
Here's the code:
#include <utility>
#include <iostream>
#include <vector>
#include <string>
int main ( )
{
std::string s = "abc";
// 1 ok
std::pair < std::string, int > a = std::make_pair ( s, 7 );
// 2 error on the next line
std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );
// 3 ok
std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );
return 0;
}
I understand that make_pair is meant to be used as the (1) case (if I specify the types, then I might as well use (3)), but I don't understand why it's failing in this case.
The exact error is:
test.cpp: In function ‘int main()’:
test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
test.cpp:11:83: note: candidate is:
In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
from test.cpp:1:
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template argument deduction/substitution failed:
test.cpp:11:83: note: cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’
Again, the question here is just "what's going on?" I know that I can fix the problem by removing the template specification, but I just want to know what's failing here under the covers.
g++ 4.4 compiles this code with no problems.
Removing -std=c++11 also compiles with code with no problems.

This is not how std::make_pair is intended to be used; you are not supposed to explicitly specify the template arguments.
The C++11 std::make_pair takes two arguments, of type T&& and U&&, where T and U are template type parameters. Effectively, it looks like this (ignoring the return type):
template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);
When you call std::make_pair and explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:
[return type] make_pair(std::string&& argT, int&& argU);
Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass, 7, because that is an rvalue expression. s, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.
So, why does it work when you don't explicitly specify what T and U are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of type A&&, where A is a template type parameter, can bind to any kind of A.
It doesn't matter whether the A is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&& can bind to that object (again, if and only if A is itself a template parameter).
In your example, we make the call:
make_pair(s, 7)
Here, s is an lvalue of type std::string and 7 is an rvalue of type int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.
To bind s, an lvalue, to T&&, the compiler deduces T to be std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. s is a match.
It's simple to bind 7 to U&&: the compiler can deduce U to be int, yielding a parameter of type int&&, which binds successfully to 7 because it is an rvalue.
There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy:
If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.
Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.

Related

Unable to assign variables to map [duplicate]

I was just playing around with g++ 4.7 (one of the later snapshots) with -std=c++11 enabled. I tried to compile some of my existing code base and one case that failed somewhat confuses me.
I would appreciate if someone can explain what is going on.
Here's the code:
#include <utility>
#include <iostream>
#include <vector>
#include <string>
int main ( )
{
std::string s = "abc";
// 1 ok
std::pair < std::string, int > a = std::make_pair ( s, 7 );
// 2 error on the next line
std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );
// 3 ok
std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );
return 0;
}
I understand that make_pair is meant to be used as the (1) case (if I specify the types, then I might as well use (3)), but I don't understand why it's failing in this case.
The exact error is:
test.cpp: In function ‘int main()’:
test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
test.cpp:11:83: note: candidate is:
In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
from test.cpp:1:
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template argument deduction/substitution failed:
test.cpp:11:83: note: cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’
Again, the question here is just "what's going on?" I know that I can fix the problem by removing the template specification, but I just want to know what's failing here under the covers.
g++ 4.4 compiles this code with no problems.
Removing -std=c++11 also compiles with code with no problems.
This is not how std::make_pair is intended to be used; you are not supposed to explicitly specify the template arguments.
The C++11 std::make_pair takes two arguments, of type T&& and U&&, where T and U are template type parameters. Effectively, it looks like this (ignoring the return type):
template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);
When you call std::make_pair and explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:
[return type] make_pair(std::string&& argT, int&& argU);
Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass, 7, because that is an rvalue expression. s, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.
So, why does it work when you don't explicitly specify what T and U are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of type A&&, where A is a template type parameter, can bind to any kind of A.
It doesn't matter whether the A is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&& can bind to that object (again, if and only if A is itself a template parameter).
In your example, we make the call:
make_pair(s, 7)
Here, s is an lvalue of type std::string and 7 is an rvalue of type int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.
To bind s, an lvalue, to T&&, the compiler deduces T to be std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. s is a match.
It's simple to bind 7 to U&&: the compiler can deduce U to be int, yielding a parameter of type int&&, which binds successfully to 7 because it is an rvalue.
There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy:
If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.
Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.

Why does invoking std::make_tuple() with explicit template arguments cause "cannot bind rvalue reference of type 'T*&&' to lvalue of type 'T*'" error? [duplicate]

I was just playing around with g++ 4.7 (one of the later snapshots) with -std=c++11 enabled. I tried to compile some of my existing code base and one case that failed somewhat confuses me.
I would appreciate if someone can explain what is going on.
Here's the code:
#include <utility>
#include <iostream>
#include <vector>
#include <string>
int main ( )
{
std::string s = "abc";
// 1 ok
std::pair < std::string, int > a = std::make_pair ( s, 7 );
// 2 error on the next line
std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );
// 3 ok
std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );
return 0;
}
I understand that make_pair is meant to be used as the (1) case (if I specify the types, then I might as well use (3)), but I don't understand why it's failing in this case.
The exact error is:
test.cpp: In function ‘int main()’:
test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
test.cpp:11:83: note: candidate is:
In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
from test.cpp:1:
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template argument deduction/substitution failed:
test.cpp:11:83: note: cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’
Again, the question here is just "what's going on?" I know that I can fix the problem by removing the template specification, but I just want to know what's failing here under the covers.
g++ 4.4 compiles this code with no problems.
Removing -std=c++11 also compiles with code with no problems.
This is not how std::make_pair is intended to be used; you are not supposed to explicitly specify the template arguments.
The C++11 std::make_pair takes two arguments, of type T&& and U&&, where T and U are template type parameters. Effectively, it looks like this (ignoring the return type):
template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);
When you call std::make_pair and explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:
[return type] make_pair(std::string&& argT, int&& argU);
Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass, 7, because that is an rvalue expression. s, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.
So, why does it work when you don't explicitly specify what T and U are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of type A&&, where A is a template type parameter, can bind to any kind of A.
It doesn't matter whether the A is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&& can bind to that object (again, if and only if A is itself a template parameter).
In your example, we make the call:
make_pair(s, 7)
Here, s is an lvalue of type std::string and 7 is an rvalue of type int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.
To bind s, an lvalue, to T&&, the compiler deduces T to be std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. s is a match.
It's simple to bind 7 to U&&: the compiler can deduce U to be int, yielding a parameter of type int&&, which binds successfully to 7 because it is an rvalue.
There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy:
If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.
Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.

rvalue reference in class template vs function template

#include <iostream>
template <typename T>
class test
{
public:
test(T&& t)
{
}
};
template <typename T>
void succeed(T&& t)
{
}
int main()
{
int i = 1;
test<int> t(i); // failed to compile
succeed(i); // OK
return 0;
}
Error from GCC 5.2:
main.cpp: In function 'int main()':
main.cpp:20:18: error: cannot bind 'int' lvalue to 'int&&'
test t(i);
^
main.cpp:7:5: note: initializing argument 1 of 'test::test(T&&) [with T = int]'
test(T&& t)
^~~~
Could someone explain why the class template cannot compile but function template is OK?
Thanks.
In succeed, T&& t is a forwarding reference, not an rvalue reference. But in test, it is an rvalue reference.
A forwarding reference happens only when the parameter is T&&, and T is a template parameter of that function. In your code T is a template parameter of the enclosing class, so it doesn't count as a forwarding reference.
A forwarding reference may bind to both lvalues and rvalues.
During the drafting of C++11 it was suggested to use different syntax for forwarding references than rvalue references (instead of using T&& t for both); however the committee eventually settled on the current behaviour.
For a more detailed description of template parameter deduction, including a more precise specification of when T&& becomes a forwarding reference, see here -- search for the term "forwarding reference" to find the special rules for forwarding references.
Your confusion is probably rooted in your assumption that in both cases T is int. This is why you presume that these two cases as similar. In reality they are not.
In the class version you are manually specifying what T is. You explicitly tell the compiler that T is int. Constructor parameter type T && in this case becomes int &&, which cannot bind to a regular lvalue. Hence the error.
In the function version you don't tell the compiler what T is, but instead you expect the compiler to deduce it. In situations like yours the language is deliberately designed to deduce T as int & (note: not as int, but rather as int &). Once T is deduced as int &, the so called "reference collapsing" rules lead to function parameter type T && becoming int & - an ordinary lvalue reference. This parameter can successfully bind to lvalue argument i.
That explains the difference you observe.
For the sake of experiment, in the latter case you can suppress template argument deduction and specify the template argument explicitly
succeed<int>(i);
That will forcefully specify T as int and lead to the very same error as in the class version for the very same reason.
Similarly, you can "simulate" function's behavior for your class by specifying the template argument as int &
test<int &> t(i);
The same "reference collapsing" rules will make your constructor invocation to compile successfully.

result_of for member object with cv-qualified argument

Given the following declarations:
struct MyClass { };
typedef int MyClass::*Mp;
On both gcc 6.2 and Clang compiler I have tried, result_of<Mp(const MyClass)>::type yields int&&.
Summary of my question: Why int&& and not either const int&& or simply int?
More Background: The standard says that result_of is defined this way:
the member typedef type shall name the type
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));
The standard also defines INVOKE for pointer-to-member-objects this way:
— t1.*f when N == 1 and f is a pointer to data member of a class T and is_base_of_v<T, decay_t<decltype(t1)>> is true;
Note that the decay_t is only for testing whether this bullet applies. As far as I can tell, applying the two points above should yield:
decltype(declval<const MyClass>().*declval<Mp>())
Which yields const int&&. So, am I missing something, or are the compiler libraries wrong?
Edit, 30 Aug 2016:
Thanks for the responses. Several people have suggested alternative ways of getting the correct result without using result_of. I should clarify that the reason I am hung up on the correct definition of result_of is that I'm actually implementing the closest reasonable implementation of result_of that works with a pre-C++11 compiler. So, while I agree that I can use decltype or result_of<Mp(const MyClass&&)>::type in C++11, they do not do what I need for C++03. Several people have given the correct answer, which is that const rvalue arguments to functions are not part of the function type. This clarifies things for me and I will implement my pre-C++11 result_of such that it also discards those qualifiers.
const is stripped from function parameters. You can verify this using is_same.
void(int) == void(const int)
Mp(MyClass) == Mp(const MyClass)
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)>
I think this is explained by [8.3.5.5]:
After producing the list of parameter types, any top-level
cv-qualifiers modifying a parameter type are deleted when forming the
function type. The resulting list of transformed parameter types and
the presence or absence of the ellipsis or a function parameter pack
is the function’s parameter-type-list. [ Note: This transformation
does not affect the types of the parameters. For example, int(*)(const
int p, decltype(p)*) and int(*)(int, const int*) are identical types.
— end note ]
You can work around it by defining your own result_of that does not (mis)use function types:
template <typename F, typename... ArgTypes>
struct my_result_of
{
using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...));
};
This definition is really what the standard should have used.
In result_of_t<Mp(const MyClass)> you appear to be trying to ask what is the type of the result of invoking Mp with a const rvalue of type MyClass. A better way to ask that with result_of would be result_of_t<Mp(const MyClass&&)> but it's usually easier to just use decltype and forget that result_of ever existed. If you actually intended to ask the result with a const lvalue then that would be result_of_t<Mp(const MyClass&)>.
It is true that top-level const on function parameters has no meaning in a function declaration. When using result_of, therefore, it makes more sense to supply argument types as references to possibly-const types. This also makes the value category explicit, with no loss of expressivity. We can use the print_type trick to see what happens when we do this:
template <typename...> struct print_type; // forward declaration
print_type<std::result_of_t<Mp(const MyClass)>,
std::result_of_t<Mp(const MyClass&)>,
std::result_of_t<Mp(const MyClass&&)>,
std::result_of_t<Mp(MyClass)>,
std::result_of_t<Mp(MyClass&)>,
std::result_of_t<Mp(MyClass&&)>>{};
This prints:
error: invalid use of incomplete type 'struct print_type<int&&, const int&, const int&&, int&&, int&, int&&>'
So we can deduce:
std::result_of_t<Mp(const MyClass)> == int&&
std::result_of_t<Mp(const MyClass&)> == const int&
std::result_of_t<Mp(const MyClass&&)> == const int&&
std::result_of_t<Mp(MyClass)> == int&&
std::result_of_t<Mp(MyClass&)> == int&
std::result_of_t<Mp(MyClass&&)> == int&&
We can see that result_of_t<Mp(const MyClass)>, result_of_t<Mp(MyClass)>, and result_of_t<Mp(MyClass&&)> all mean the same thing. I would find it surprising if they didn't.
Note that when you use declval you are also providing argument types as references, as declval is declared to return a reference. Furthermore, all parameters to std::invoke are references.

C++11 make_pair with specified template parameters doesn't compile

I was just playing around with g++ 4.7 (one of the later snapshots) with -std=c++11 enabled. I tried to compile some of my existing code base and one case that failed somewhat confuses me.
I would appreciate if someone can explain what is going on.
Here's the code:
#include <utility>
#include <iostream>
#include <vector>
#include <string>
int main ( )
{
std::string s = "abc";
// 1 ok
std::pair < std::string, int > a = std::make_pair ( s, 7 );
// 2 error on the next line
std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );
// 3 ok
std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );
return 0;
}
I understand that make_pair is meant to be used as the (1) case (if I specify the types, then I might as well use (3)), but I don't understand why it's failing in this case.
The exact error is:
test.cpp: In function ‘int main()’:
test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
test.cpp:11:83: note: candidate is:
In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
from test.cpp:1:
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
/gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template argument deduction/substitution failed:
test.cpp:11:83: note: cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’
Again, the question here is just "what's going on?" I know that I can fix the problem by removing the template specification, but I just want to know what's failing here under the covers.
g++ 4.4 compiles this code with no problems.
Removing -std=c++11 also compiles with code with no problems.
This is not how std::make_pair is intended to be used; you are not supposed to explicitly specify the template arguments.
The C++11 std::make_pair takes two arguments, of type T&& and U&&, where T and U are template type parameters. Effectively, it looks like this (ignoring the return type):
template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);
When you call std::make_pair and explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:
[return type] make_pair(std::string&& argT, int&& argU);
Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass, 7, because that is an rvalue expression. s, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.
So, why does it work when you don't explicitly specify what T and U are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of type A&&, where A is a template type parameter, can bind to any kind of A.
It doesn't matter whether the A is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&& can bind to that object (again, if and only if A is itself a template parameter).
In your example, we make the call:
make_pair(s, 7)
Here, s is an lvalue of type std::string and 7 is an rvalue of type int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.
To bind s, an lvalue, to T&&, the compiler deduces T to be std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. s is a match.
It's simple to bind 7 to U&&: the compiler can deduce U to be int, yielding a parameter of type int&&, which binds successfully to 7 because it is an rvalue.
There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy:
If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.
Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.