Okay, this may seem like a silly question, but here it goes:
template <typename T>
void foo(T& x)
{
}
int main()
{
foo(42);
// error in passing argument 1 of 'void foo(T&) [with T = int]'
}
What is preventing C++ to instantiate the foo function template with T = const int instead?
The problem is that template type deduction has to work out an exact match, and in that particular case, because of the reference in the signature, an exact match requires an lvalue. The value 42, is not an lvalue, but rather an rvalue, and resolving T with const int would not yield a perfect match. Since template type deduction is limited to exact matches, that deduction is not allowed.
If instead of using a literal you use a non mutable lvalue, then the compiler will deduce the type appropriatedly, as const int will become a perfect match for the argument:
const int k = 10;
foo( k ); // foo<const int>( const int & ) is a perfect match
Now there is a special rule that enables calling a function that takes a const reference (nonmutable lvalue) with an rvalue, that implies creation of a temporary lvalue which is later bound to the reference, but for that rule to kick in the function has to have that signature before hand, which is why explicitly stating that the type of the template is const int works: foo<const int>(42).
Them's the rules ;-). If you leave the compiler to deduce the type from the argument, it picks the simplest thing it can.
It doesn't seem unreasonable to me. Your template is saying it expects a non-const reference, so it doesn't compile with an rvalue.
You could either tell it what you mean at the call site: foo<int const>(42); or change your template to make it clear it doesn't need a mutable reference: template <typename T> void foo(T const & x) { }.
In C++11 you have more options for expressing what your template will and will not accept.
Be it template or normal functions, rvalue cannot be passed by reference. (so const T& works but not T&).
What is preventing C++ to instantiate the foo function template with T = const int instead?
Suppose, C++ allows and makes T = const int instead.
Now after sometime you change foo as,
template<typename T>
void foo (T& x)
{
x = 0;
}
Now compiler has to generate error. For end user the experience will be strange, as for a valid statement like x = 0; it started giving error. That could be the reason that why compiler prevents at the first stage itself!
Related
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void wrapper(T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error
}
Hi I have a question on why the last statement gives a compiler error.
:18:10: error: cannot bind non-const lvalue reference of type
'A&' to an rvalue of type 'A' wrapper(A());
I thought that the template type T would be deduced as const A& as the ca is also deduced as const A&. Why the type deduction fails in this case?
I thought that the template type T would be deduced as const A& as the ca is also deduced as const A&. Why the type deduction fails in this case?
Because that's not how the deduction rules work. They strive to deduce as much of a match to the function argument type as possible. A temporary is not necessarily const, it can just bind to a const reference.
But your function template does not accept by a const reference, instead it accepts by a non-const lvalue reference. So no const is gonna spring up unless the function argument is const itself (which it isn't).
The proper solution is to use a forwarding reference (an rvalue reference to a deduced template parameter):
template <typename T>
void wrapper(T&& u)
{
g(std::forward<T>(u));
}
Now u can bind to any object. And the types deduced will tell you the value category of the function argument, allowing you to forward that category to the function call g(...), and making sure the proper overload is picked.
By the way, in case you are curious, if you add the const directly to the temporary type, your original code will build just fine.
using CA = A const;
wrapper(CA()); // Not an error anymore
Now u ends up being a A const& and binds to the temporary just fine. But that is just a curiosity that's unlikely to be useful in practice. Use forwarding references.
Why the type deduction fails in this case?
It does not fail. T is deduced to A. Therefore, the parameter u is of type A&.
The call to wrapper() is what actually fails, since an rvalue (i.e.: A() in this case) can't be bound to a non-const lvalue reference (i.e.: the parameter u).
The error message is trying to convey:
A non-const reference cannot bind to a temporary value because the temporary object's lifetime would have expired before the control reaches the function.
The variable ca is not a temporary, because it has a name that can be referred to in main.
It's lifetime does not expire till the end of main, and so can be safely referenced from within wrapper.
Template type deduction does not drop const since it would be a violation of const-correctness for ca.
For wrapper(A());, the type parameter T would be deduced as A. Since the temporary has no const-ness, so the parameter u would be deduced to a A&, but a since non-const reference cannot bind to a temporary value for the reason mentioned above, there is no wrapper function that is viable to be called using the argument A().
Now, C++ features the extension of lifetime for a temporary object if you are only reading from it. This is the reason why const T& binds to temporary objects. In your case the nameless temporary object's lifetime is extended till the end of the wrapper function.
If you explicitly set the type parameter to be const T& the following:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
class A {};
void g(const A& a) {}
int main()
{
const A ca;
wrapper(ca);
wrapper(A()); // Error no more.
}
would compile just fine.
[C++11]
For wrapper(A());, the type parameter T would still be deduced as A, and the parameter u would be of type A&&, called an rvalue reference to A.
Suppose we add another overload to wrapper so that the following:
template <typename T>
void wrapper(const T& u)
{
g(u);
}
template <typename T>
void wrapper(T&& u)
{
g(u);//u is an lvalue since it has a name.
}
exists.
You can expect wrapper(A()); to compile, preferring the rvalue overload, because the rvalue reference is an lvalue in wrapper as it has a name.
Consider:
template <typename T> void f(T&);
const int b = 2;
f(b); // ok
f(2); // error, can not bind rvalue to lvalue reference
Why is f(const int) allowed? the logic seems to indicate that if the programmer did not explicitly define the template parameter as const T&, he/she wanted to modify the binded-to variable.
So in this case the question is, why does template instantiation give itself the freedom to instantiate with consts when those were not explicitly required ?
Even if there was a rationale for allowing template instantiation to instantiate with consts, then why, in that case, would be binding to rvalues forbidden? you can bind rvalues to const lvalue references. In that case, the template would be instantiated to f<const int>, and f(2) would be allowed.
I want to know the reasoning behind these decisions, not references to the standard.
Why is f(const int) allowed?
You might subsitute the template T by const int to transform T& into const int&
Even if there was a rationale for allowing template instantiation to instantiate with consts, then why, in that case, would be binding to rvalues forbidden?
There is no T (in T&) which (exact) matches int&& for deduction.
f<const int>(42) is allowed, but there is no deduction happening.
I want to know the reasoning behind these decisions, not references to the standard.
So why allowing cv substitution in template?
I would say it make generic programming easier.
Else you would have to provide overloads for each combination of const, volatile.
Here, if you want to restrict T to non const, you may use SFINAE with traits:
template <typename T> std::enable_if_t<!std::is_const<T>::value> f(T&);
So in this case the question is, why does template instantiation give itself the freedom to instantiate with consts when those were not explicitly required?
Quite simply nothing in the implementation of f prevents instantiating the template with const int. Try the following instead and you'll observe a problem.
template <typename T> void f(T& v) { v = 3; };
const int b = 2;
f(b); // error: assignment of read-only reference (within the template instance)
int c = 2;
f<const int>(c); // error
The fact that b is const int means the template is instantiated as <const int> and this is not possible because v is modified. Explicit instantiation as <const int> is similarly disallowed.
Why is it deduced as const int? It should have been deduced as int.
When the type to instantiate the template is not explicitly specified, it is deduced based on the type passed into the function which is const int. Expecting it deduce a different type is impractical (if not outright absurd).
Please also note that if it did deduce the type as int then there would be an error passing const int into the function. As can be demonstrated by explicitly instantiating the template as <int>.
template <typename T> void f(T&) {};
const int b = 2;
f<int>(b); //error: no matching function for call to 'f<int>(const int&)
why, in that case, would be binding to rvalues forbidden?
The compiler would need to implicitly assume an appropriate type to use for instantiating the template and then perform the implicit conversion from rvalue to const lvalue reference. (Seems a a stretch expectation to me.)
Note that if you explicitly instantiate the template as <const int> then binding rvalue to const lvalue reference works because the conversion can be done.
template <typename T> void f(T&);
f<const int>(2); // OK
I have a template function:
template<typename T>
void doSomething(T& value) {
// doSomething here
}
All is ok, but passing r-value references:
doSomething(getTempVal());
Producing no matching function for call error, because particular instantiated function template expects an l-value for 1st argument. Is there any workarounds to allow template function taking both lvalue and rvalue references without adding new template?
Yes, just use && instead of &.
It may seem odd, because you might think that this would disallow calling it with lvalues, but actually, if T is itself an lvalue reference type, then T && is just T: it doesn't become an rvalue reference type.
In other words,
template <typename T>
void f(T &&);
int main() {
int k = 1;
f(k); // okay: calls f<int&>
f(2); // okay: calls f<int>
}
Note that T can be deduced as a reference type, and the function body will need to be made to handle that.
I'm learning about templates and tried to implement this method:
template <typename Func, typename Left, typename Right>
void flipArgs(Func* function, Left&& leftArg, Right&& rightArg) {
function(std::forward<Right>(rightArg), std::forward<Left>(leftArg));
}
It takes a function and two parameters and calls the given function with the two parameters flipped.
It works fine with function such as:
void test1(std::string, int) {
}
When I tried this function:
template <typename T>
void test2(T&& a, int) {
}
With:
string s("test");
flip(test2<string>, 42, s);
The compiler (g++ 4.7.1) tells me:
error: cannot bind 'std::basic_string' lvalue to 'std::basic_string&&'
I thought that a function parameter such as T&& was a special case that can bind to rvalue and lvalue references? What am I doing wrong?
I thought that a function parameter such as T&& was a special case that can bind to [rvalues and lvalues]?
It is. It basically means the template can have different instantiations for lvalues and for rvalues.
However... When you explicitly make T be string in test2<string>, you are picking one particular instantiation: void test2(string&&, int). string&& is no longer that special case. string&& can only bind to string rvalues. There isn't one instantiation that binds to both rvalues and lvalues.
In general, I'd recommend against explicitly passing function template parameters (unless those are intended, like std::forward or std::make_unique).
In this case, you could instead force one of the instantiations that binds to lvalues. Something like flip(test2<string&>, 42, s);, which will instantiate void test2(string&, int).
If you really want to pass an argument to flip that can accept both lvalues and rvalues, you need a polymorphic function object:
struct test2 {
template <typename T>
void operator()(T&& a, int) const {
}
};
flip(test2{}, 42, s);
The key here is that the decision of which specialisation to use is not made when passing the argument, but only later on when that argument is used.
For completeness, in C++14 you can actually create anonymous polymorphic function objects with the new lambda syntax:
auto test2 = [](auto&& a, int) {};
flip(test2, 42, s);
I'm looking at [VC10's] unique_ptr and they do a couple things I don't understand:
typedef typename tr1::remove_reference<_Dx>::type _Dx_noref;
_Dx_noref& get_deleter()
{ // return reference to deleter
return (_Mydel);
}
unique_ptr(pointer _Ptr,
typename _If<tr1::is_reference<_Dx>::value, _Dx,
const typename tr1::remove_reference<_Dx>::type&>::_Type _Dt)
: _Mybase(_Ptr, _Dt)
{ // construct with pointer and (maybe const) deleter&
}
typename tr1::add_reference<_Ty>::type operator*() const
{ // return reference to object
return (*this->_Myptr);
}
Wouldn't just writing _Dx& or _Ty& be the same thing?
I actually do understand why they did it here though:
unique_ptr(pointer _Ptr, typename tr1::remove_reference<_Dx>::type&& _Dt)
: _Mybase(_Ptr, _STD move(_Dt))
{ // construct by moving deleter
}
get_deleter
Any reference is removed from the return type, then a reference is added back. In conformant C++11, adding a & to an existing & (or &&) produces a &. In C++03 however, that would be forming a reference to reference type, which was illegal. Likely MSVC is using the old rules, or that code was written when it did and remains because it is harmless.
constructor
Here they remove the reference, add const, and then add the reference back, to be passing by const reference. This is because adding const directly to a reference type does nothing! (ยง8.3.2/1) In either C++11 or C++03, the parameter declaration would be valid but would not add a const, if the reference weren't removed and replaced.
operator*
This is essentially the same as get_deleter, but they went about it a different way, and _Ty cannot be a reference type to begin with. It looks to me like _Ty& would suffice, but it's their prerogative.
Here's an example, possibly archetypal, for why we need remove_reference, in the implementation of std::move: The goal is to return an rvalue-reference type, based on the deduced type of the function argument.
Example: Foo x; move(x); Here move(x) should return a type Foo&&. But the argument of move is an expression of type Foo&. So how can the move function deduce the right type?
The first attempt is to use ordinary template argument deduction and use a cast:
template <typename T> T && move(??? x) { return static_cast<T&&>(x); }
But what should go into ???? If we say T x, then T will be deduced as Foo&; if we say T & x, then T = Foo, and if we say T && x, it won't match at all. The second version, T & x, appears to be useful.
But then the function doesn't work on rvalues to begin with (e.g. move(Foo(...)). In this case, we want T && x so that T = Foo and T&& = Foo&& as desired. We could have two overloads, but having multiple overloads is undesirable because it increases complexity needlessly. And finally, if someone were to specify the template paramete explicitly as move<Foo&>(x), the function would never work, because when T = Foo&, then T&& = Foo& as well.
So in comes remove_reference:
template <typename T>
typename std::remove_reference<T>::type && move(T && x)
{
return static_cast<typename std::remove_reference<T>::type &&>(x);
}
First off, the new reference collapsing rules imply that T is deduces as either Foo& or Foo&& in the two cases. Then, remove_reference strips the reference and gives type Foo in any case, and adding && makes the desired Foo&& return type.
In an oversimplified summary: we need remove_reference because (Foo&)&& is Foo& and not Foo&&. If you ever write template code that needs the base type of a template paramter that could be deduced as either U& or U&&, you can use this model.
template class add_reference has specialization for void, const void and const volatile void because reference of void type (void&) is not allowed. If _Ty& is used, it would generate a compilation error when _Ty = void.