I'm learning C++ and have a little problem with template specialization. I need to call Variable.Set() a lot so, I made the function take in references so it doesn't spend a lot of time copying the string. But then the problem I have is Variable.Set<int>(5); causes error because the parameter is an rvalue and I don't know the solution for this.
error C2664: 'void Variable::Set(int &)': cannot convert argument 1 from 'int' to 'int &'
void main()
{
Variable var;
var.Set<int>(5);
}
struct Variable
{
int integer;
float floating;
std::string string;
template<typename T> void Set(T& v);
template<> void Set<int> (int& v) { integer = v; }
template<> void Set<float> (float& v) { floating = v; }
template<> void Set<std::string> (std::string& v) { string = v; }
};
You need to change your parameters to constant references (const& as mentioned in Praetorian's comment)
From this link: http://www.codesynthesis.com/~boris/blog/2012/07/24/const-rvalue-references/
while a const lvalue reference can bind to an rvalue, a const
rvalue reference cannot bind to an lvalue
Related
I've reduced my code down to the following minimum code:
#include<iostream>
#include<vector>
class tt
{
public:
bool player;
std::vector<tt> actions;
};
template<typename state_t>
int func(state_t &state, const bool is_max)
{
state.player = true;
const auto &actions = state.actions;
if(state.actions.size())
{
auto soln = func(actions[0], false);
}
return 0;
}
int main(int argc, char const *argv[])
{
tt root;
func(root, true);
return 0;
}
When I try to compile this code, I get
test.cpp:14:17: error: cannot assign to variable 'state' with const-qualified type 'const tt &'
state.player = true;
~~~~~~~~~~~~ ^
test.cpp:19:19: note: in instantiation of function template specialization 'func<const tt>' requested here
auto soln = func(actions[0], false);
^
test.cpp:28:4: note: in instantiation of function template specialization 'func<tt>' requested here
func(root, true);
^
test.cpp:12:19: note: variable 'state' declared const here
int func(state_t &state, const bool is_max)
~~~~~~~~~^~~~~
1 error generated.
It is claiming that state is a const tt & type. The signature of the templated function is int func(state_t &state, const bool is_max), and there is no const in front of the state_t. It appears the const is somehow being deduced from the recursive call because actions is a const-ref vector of tt objects. I thought argument deduction ignores const? How can this occur?
Answer is mainly extracted from Scott Mayers Effective C++ book.
template<typename T>
void f(ParamType param);
f(expr); // deduce T and ParamType from expr
ParamType is a Reference or Pointer, but not a Universal Reference
The simplest situation is when ParamType is a reference type or a pointer type, but not a universal reference. In that case, type deduction works like this:
If expr’s type is a reference, ignore the reference part.
Then pattern-match expr’s type against ParamType to determine T.
In argument deduction process it ignores the reference part not the const part.
In your case it is const auto &actions = state.actions; which means, for the template argument deduction of auto soln = func(actions[0], false); only the reference part is dropped, not the cv qualifiers.
Further examples from the book.
template<typename T>
void f(T& param); // param is a reference
and we have these variable declarations,
int x = 27; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
the deduced types for param and T in various calls are as follows:
f(x); // T is int, param's type is int&
f(cx); // T is const int,
// param's type is const int&
f(rx); // T is const int,
// param's type is const int&
In addition to #aep's answer, If it had been deduced as tt instead of const tt, the compiler would generated an error too, because it is not possible to bind const reference to non-const reference without const_cast.
#include<iostream>
#include<vector>
class tt
{
public:
bool player;
std::vector<tt> actions;
};
void try_to_bind_const_reference_to_non_const_reference( tt& t )
{
}
template<typename state_t>
int func(state_t &state, const bool is_max)
{
state.player = true;
const auto &actions = state.actions;
if(state.actions.size())
{
// auto soln = func(actions[0], false);
try_to_bind_const_reference_to_non_const_reference( actions[0] );
}
return 0;
}
int main(int argc, char const *argv[])
{
tt root;
func(root, true);
return 0;
}
run online
I have the following code:
template <typename T>
struct Val
{
T val;
const T& get() const { return val; }
void set(const T& newVal) { val = newVal; }
};
int& f(const Val<int>& val)
{
return val.get();
}
int* g(const Val<int*>& val)
{
return val.get();
}
int main()
{
Val<int> val1;
f(val1);
Val<int*> val2;
g(val2);
return 0;
}
It fails compilation in gcc with the following message:
main.cpp: In function 'int& f(const Val<int>&)':
main.cpp:78:20: error: invalid initialization of reference of type 'int&' from expression of type 'const int'
return val.get();
^
main.cpp:79:1: warning: control reaches end of non-void function [-Wreturn-type]
That's totally fine, but why isn't the same error produced for g()? Why, for some reason, is the const qualifier removed on const T&, when T is a pointer?
I tried to find some resources, but it seems hard to find. I know Meyers wrote something in his newest book, but I don't have access to that one. Can someone point me to resources or keywords where I could find more on that whole topic of template type deduction when T is a pointer?
Why, for some reason, is the const qualifier removed on const T&, when T is a pointer?
Because you're returning the pointer by value, not by reference. It is perfectly fine to initialize a pointer from a const pointer (note it is the pointers that are const, not the things they point to.) You're doing the equivalent of this:
int* const p0 = nullptr; // const pointer
int* p1 = p0; // copy to non-const is OK
If you were to initialize a non-cost reference to poiter from a const pointer, you'd get a similar error to the first case:
// error: binding of reference to type 'int *'
// to a value of type 'int *const' drops qualifiers
int*& p1 = p;
i have a class with a overladed operators.
class sout {
public:
template<typename T>
friend inline sout& operator&(sout&, T&);
friend inline sout& operator&(sout&, std::string&);
};
now if i use the templated operator& inside the the sting.operator& i get an error:
code:
sout& operator&(sout& s, std::string& str) {
uint16_t ts = static_cast<uint16_t>(str.size()); // this is ok
s & ts; // is also ok
s & static_cast<uint16_t>(str.size()); // but this is wrong
// ...
return s;
}
error:
Error:C2679: binary '&' : no operator found which takes a right-hand operand of type 'uint16_t' (or there is no acceptable conversion)
could be 'sout &operator &<uint16_t>(sout &,T &)'
with
[
T=uint16_t
]
or 'sout &operator &(sout &,std::string &)'
while trying to match the argument list '(sout, uint16_t)'
than i tried to use the explicite operator& template-type by:
operator&<uint16_t>(s, ts); // this also ig ok
but if i combine it, i again a error:
operator&<uint16_t>(s, static_cast<uint16_t>(str.size())
error:
'operator &' : cannot convert parameter 2 from 'uint16_t' to 'uint16_t &'
i also tried reinterpret_cast.
i know operator& is expecting a reference to uint16_t and the size() function is returning a size_t (int) not a reference. is it possible to do that in one line?
The problem is that the value returned by size() is a temporary, and temporaries are rvalues; however, your function accepts an lvalue reference. The following snippet clarifies the problem:
int foo() { return 42; }
void bar(int& i) { i++; } // Parameter is an lvalue reference to non-const
int main()
{
bar(foo()); // ERROR! Passing an rvalue to bar()
bar(1729); // ERROR! Passing an rvalue to bar()
int i = 42;
bar(i); // OK! Passing an lvalue to bar()
}
lvalue references cannot bind to rvalues, unless they are references to const.
template<typename T>
friend inline sout& operator&(sout&, T const&);
// ^^^^^
If your operator& is supposed to modify the right hand argument, so that the reference cannot be a reference to const, in C++11 you may use rvalue references (this will allow to bind to lvalues as well due to C++11's reference collapsing rules):
template<typename T>
friend inline sout& operator&(sout&, T&&);
// ^^^
#include<iostream>
template<class T>
struct Foo
{
T v_;
Foo(T&& v):v_(std::forward<T>(v))
{
std::cout << "building Foo..\n";
}
};
int main()
{
int v;
Foo<int> foo(v);
std::cin.ignore();
}
visual c++ 2010 output :
error C2664: 'Foo<T>::Foo(T &&)' : cannot convert parameter 1 from 'int' to 'int &&'
Is it normal that I can't bind a lvalue to a rvalue reference ?
EDIT:
same thing for a function :
void f(int && v)
{
}
int v;
f(v); //won't compile
I'm a little bit confused because I think std::forward is usefull to detect if a method is called with a lvalue or a rvalue reference.
But it seems that we can't bind any lvalue into rvalue reference... at least without template parameter but I don't understand it very well
It's normal that calling a move constructor requires an r-value, not an l-value, since an l-value could be named after the move constructor runs, and using a moved-from value would evoke unintended behavior.
Given GMan's deliciously evil auto_cast utility function concocted here, I've been trying to figure out why it doesn't compile for me when I'm trying to auto_cast from an rvalue (on MSVC 10.0).
Here's the code that I'm using:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(pX) { }
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
int main()
{
int c = auto_cast( 5.0f ); // from an rvalue
}
To the best of my ability I've tried to follow the C++0x reference collapsing rules and the template argument deduction rules outlined here, and as far as I can tell the code given above should work.
Recall that in pre-0x C++, it is not allowed to take a reference to a reference: something like A& & causes a compile error. C++0x, by contrast, introduces the following reference collapsing rules:
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
The second rule is a special template argument deduction rule for function templates that take an argument by rvalue reference to a template argument:
template<typename T>
void foo(T&&);
Here, the following rules apply:
When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&.
When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&.
Now when I mouse over the call to auto_cast( 5.0f ), the tooltip correctly displays its return value as auto_cast_wrapper<float>. This meaning that the compiler has correctly followed rule 2:
When foo is called on an rvalue of type A, then T resolves to A.
So since we have an auto_cast_wrapper<float>, the constructor should instantiate to take a float&&. But the error message seems to imply that it instantiates to take a float by value.
Here's the full error message, showing again that T=float correctly yet the T&& parameter becomes T?
main.cpp(17): error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
You cannot bind an lvalue to an rvalue reference
main.cpp(17) : while compiling class template member function 'auto_cast_wrapper<T>::auto_cast_wrapper(T &&)'
with
[
T=float
]
main.cpp(33) : see reference to class template instantiation 'auto_cast_wrapper<T>' being compiled
with
[
T=float
]
Any thoughts?
You forgot to std::forward the T&& argument to the auto_cast_wrapper constructor. This breaks the forwarding chain. The compiler now gives a warning but it seems to work fine.
template <typename T>
class auto_cast_wrapper
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }
auto_cast_wrapper(const auto_cast_wrapper&);
auto_cast_wrapper& operator=(const auto_cast_wrapper&);
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
float func() {
return 5.0f;
}
int main()
{
int c = auto_cast( func() ); // from an rvalue
int cvar = auto_cast( 5.0f );
std::cout << c << "\n" << cvar << "\n";
std::cin.get();
}
Prints a pair of fives.
Sorry for posting untested code. :)
DeadMG is correct that the argument should be forwarded as well. I believe the warning is false and the MSVC has a bug. Consider from the call:
auto_cast(T()); // where T is some type
T() will live to the end of the full expression, which means the auto_cast function, the auto_cast_wrapper's constructor, and the user-defined conversion are all referencing a still valid object.
(Since the wrapper can't do anything but convert or destruct, it cannot outlive the value that was passed into auto_cast.)
I fix might be to make the member just a T. You'll be making a copy/move instead of casting the original object directly, though. But maybe with compiler optimization it goes away.
And no, the forwarding is not superfluous. It maintains the value category of what we're automatically converting:
struct foo
{
foo(int&) { /* lvalue */ }
foo(int&&) { /* rvalue */ }
};
int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue
And if I'm not mistaken the conversion operator shouldn't be (certainly doesn't need to be) marked const.
The reason it doesn't compile is the same reason for why this doesn't compile:
float rvalue() { return 5.0f }
float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
As a is itself an lvalue it cannot be bound to b. In the auto_cast_wrapper constructor we should have used std::forward<T> on the argument again to fix this. Note that we can just use std::move(a) in the specific example above, but this wouldn't cover generic code that should work with lvalues too. So the auto_cast_wrapper constructor now becomes:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
...
private:
auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }
T&& mX;
};
Unfortunately it seems that this now exhibits undefined behaviour. I get the following warning:
warning C4413: 'auto_cast_wrapper::mX' : reference member is initialized to a temporary that doesn't persist after the constructor exits
It appears that the literal goes out of scope before the conversion operator can be fired. Though this might be just a compiler bug with MSVC 10.0. From GMan's answer, the lifetime of a temporary should live until the end of the full expression.