#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.
Related
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.
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.
#include <type_traits>
template<typename T>
void f(const T&)
{
static_assert(std::is_array_v<T>); // ok
}
template<typename T>
void g(T&&)
{
static_assert(std::is_array_v<T>); // error
}
int main()
{
char arr[8];
f(arr); // ok
g(arr); // error
}
My compiler is clang 7.0 with -std=c++17.
Why doesn't universal reference apply for arrays?
First of all, these are officially called "forwarding references", not "universal references".
Your static_assert fails due to the fact that T is deduced as T& when passing an lvalue to a function taking a "forwarding reference" - this is one of the special rules of "forwarding references" that apply during template argument deduction.
You can fix your assert by stripping any reference out first:
static_assert(std::is_array_v<std::remove_cvref_t<T>>);
live example on godbolt.org
std::remove_cvref_t is a bleeding edge C++20 feature - you might want to use std::remove_reference_t instead if your compiler doesn't support it.
The relevant rule here when the template argument deduction takes place is:
Deduction from a function call
...
4. If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction.
So in your case, arr is being deduced as reference to arr and thus the static_assert fails.
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.
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.