template<class U>
void f( U && v)
{
std::cout << typeid(v).name() << "\n"; //'int' in both cases
if( boost::is_same<int&&,U>::value )
{
std::cout << "reach here\n"; //only with f<int&&>(int(1));
}
}
int main()
{
f(int(1));
f<int&&>(int(1));
std::cin.ignore();
}
Why v parameter is interpreted as int when I don't explicitly use f<int&&>?
What is the difference ? (Compiled with MVS2010)
My guess is that First is passed as a rvalue and second as a rvalue reference and both bound correctly into a rvalue reference, am I right ?
Thanks.
No, not really. An rvalue reference is never deduced. The notion U&& with U being a deducible template parameter is used to indicate that U should be deduced such that the rvalue-ness of the argument is retained:
when passing an rvalue of type X the type of U becomes X.
when passing a cv qualified lvalue of type X then U becomes the type X cv&.
The more interesting question is what happened to the rvalue references explicitly specified in the second call because there is no deduction going on because in this case the two rvalue references are collapsed into just one.
First variant
f(int(1)) <=> f<int>(int(1)) <=> U==int <=> is_same<int&&,int> == false
Second variant
f<int&&>(int(1)) <=> U==int&& is_same<int&&,int&&> == true
Like this
Related
I would like to go over an example using std::forward because sometimes I can make it work and some other times I can’t.
This is the code
void f(int&& int1, int&& int2){
std::cout << "f called!\n";
}
template <class T>
void wrapper(T&& int1, T&& int2){
f(std::forward<T>(int1), std::forward<T>(int2));
}
int main(){
int int1 = 20;
int int2 = 30;
int &int3 = int1;
wrapper(int1, int2);
}
I am passing int 1 and int 2. These are lvalues. They are silently converted to &int1, &int2. These are converted using &&. But reference collapsing keeps them just &int1, &int2.
f takes && parameters
If I pass simply int1 and int2 as they are I am passing &int1, &int2. This does not work.
So I pass std::forward(int1) std::forward(int2).It should be the same as using static_cast<T&&>. Because of this, thanks to referencing collapsing I can pass to every function f (theoretically even one that accepts only l-value references).
My code does not compile and my logical reasoning has probably some contradictions.
candidate function not viable: no known conversion from 'int' to 'int &&' for 1st argument
void f(int&& int1, int&& int2){
How on earth did I get a simple int after using all these ampersands?
Additional question: My compiler asks me to use wrapper<int &> instead of only wrapper(some parameters). Can I just leave it like in my code, or I need to manually put wrapper<int &> (this is what my compiler is asking me to add). Why do I need <int &> int this case?
The whole problem stems from the forwarding references using same symbols as rvalue ones, but not being the same.
Take the following code:
template<typename T>
void f(T&& t)
{
//whatever
}
In this case T&& is a forwarding reference. It is neither T&& in the sense of rvalue-reference (reference to temporary), nor is it T&. Compiler deduces that at compile time. Notice though, it does not have type specified either, it's a template paremeter. Only in case of those parameters the forwarding reference semantics applies (another way of writing it down is a auto&& in lambdas, but the deduction works the same way).
Thus when you call int x= 3; f(x); you're effectively calling f(int&). Calling f(3) calls effectively f(int&&) though.
void g(int&& arg)
arg is and rvalue reference to int. Because the type is specified, it's not a template argument! It's always an rvalue reference.
Thus, for your case
void f(int&& int1, int&& int2){
std::cout << "f called!\n";
}
template <class T>
void wrapper(T&& int1, T&& int2){
f(std::forward<T>(int1), std::forward<T>(int2));
}
int main(){
int int1 = 20;
int int2 = 30;
int &int3 = int1;
wrapper(int1, int2); //call wrapper(int&, int&);
//Then f(std::forward<T>(int1), std::forward<T>(int2));-> f(int&, int&), because they are lvalues! Thus, not found, compilation error!
}
Live demo: https://godbolt.org/z/xjTnjcqj8
forward is used to convert the parameter back to the "valueness" it had when passed to the function. We can see how this works using this example
void foo(int&) { std::cout << "foo(int&)\n"; }
void foo(int&&) { std::cout << "foo(int&&)\n"; }
template <typename T> void wrapper(T&& var) { foo(std::forward<T>(var)); }
int main()
{
int bar = 42;
wrapper(bar);
wrapper(42);
}
which outputs
foo(int&)
foo(int&&)
So, when you pass wrapper an lvalue, forward will forward that along, and the lvalue accepting overload of foo is called. When you pass an rvalue to wrapper, forward will convert var back into an rvalue1 and the rvalue overload of foo is called.
Since your f function only accepts rvalues, that means your wrapper function will also only work for rvalues. You are basically just trying to do f(int1, int2) in main, and that wont work.
The reason you get the error message no known conversion from 'int' to 'int &&' is that it is trying to tell you that there is no conversion from an lvalue int into a reference to an rvalue int.
1: This is needed because as a named variable, it is an lvalue, even if it is a reference to an rvalue.
I'll start off with something mentioned in the comments, and that is that having only one template parameter here relates the two parameters and interferes with the usual deduction process. Normally when forwarding, you deduce a type for each forwarded parameter independently. In this case, it will still work, but only because the call site passes two things with the same type and the same value category (lvalue, rvalue, etc.).
Here's what a typical wrapper would look like, truly forwarding the arguments independently:
template <class T, class T2>
void wrapper(T&& int1, T2&& int2){
f(std::forward<T>(int1), std::forward<T2>(int2));
}
With that out of the way, I'll move on to the intuitive reason your code doesn't compile. If forwarding is done correctly, the wrapper function's existence won't change anything. By the above reasoning, your wrapper function meets this criteria for this particular call. Let's see what happens when it's gone:
void f(int&& int1, int&& int2){
std::cout << "f called!\n";
}
int main(){
int int1 = 20;
int int2 = 30;
int &int3 = int1;
f(int1, int2);
}
You might be able to spot the error more clearly now. f takes rvalues, but it's being given lvalues. The wrapper preserves the value category of its arguments, so they're still lvalues when handed to f. To fix this with the wrapper, it's the same as without—pass rvalues:
int main(){
f(20, 30);
// Alternatively: f(std::move(int1), std::move(int2));
}
Now, reviewing the points made:
I am passing int 1 and int 2. These are lvalues. They are silently converted to &int1, &int2. These are converted using &&. But reference collapsing keeps them just &int1, &int2.
Don't think about the variables themselves here, but the types. Because you pass in two lvalue ints, T is deduced to be int&. Following that, the actual parameter types, T&&, are then int& as well because of reference collapsing. Thus, you have a function stamped out with two int& parameters. In the forward calls, T is int&, so its reference-collapsed return type is again int&. Thus, the call expressions have the type int and are lvalues (specifically because the return type is int&—the language calls that out as an explicit rule).
As a side note, &int1 isn't clear to me because its C++ meaning is taking the address of int1, an entirely irrelevant meaning here. I think what you're trying to say is that it's an lvalue or that a parameter's type is an lvalue reference.
If I pass simply int1 and int2 as they are I am passing &int1, &int2. This does not work.
This is true for the the reasons discussed earlier. The parameters are themselves lvalues, so a function taking rvalues won't accept them.
So I pass std::forward(int1) std::forward(int2).It should be the same as using static_cast<T&&>. Because of this, thanks to referencing collapsing I can pass to every function f (theoretically even one that accepts only l-value references).
Yes, this is the point of forwarding. It lets you preserve the value category of the arguments given to the wrapper, so the same template can be used to pass along arguments to a function regardless of which value categories it accepts. It's up to the caller of the wrapper to provide the correct value category in the first place and then the wrapper simply promises not to mess with it.
How on earth did I get a simple int after using all these ampersands?
This is just how the type is displayed in the error message. Although the parameter is an int&, the expression has the type int. Remember that an expression has both a type and a value category and that they're separate properties. For example, you can have an rvalue reference parameter int&& x and the expression x will still be an int and an lvalue.
Referring to std::forward
For all the overloads, the return type has been specified as T&& (ignoring constexpr).
But in the description attached to the following example:
template<class T>
void wrapper(T&& arg)
{
// arg is always lvalue
foo(std::forward<T>(arg)); // Forward as lvalue or as rvalue, depending on T
}
If a call to wrapper() passes an rvalue std::string, then T is
deduced to std::string (not std::string&, const std::string&, or
std::string&&), and std::forward ensures that an rvalue reference is
passed to foo.
If a call to wrapper() passes a const lvalue
std::string, then T is deduced to const std::string&, and
std::forward ensures that a const lvalue reference is passed to foo.
If a call to wrapper() passes a non-const lvalue std::string, then T
is deduced to std::string&, and std::forward ensures that a non-const
lvalue reference is passed to foo.
In the two instances above after the first, an lvalue reference and not an rvalue reference (as implied by T&&, is this understanding correct?) has been documented as being passed to foo.
If the above understanding is correct, how come the return value has been specified as T&&?
There is a distinction between
void f1(int&& a) {}
template<class T>
void f2(T&& a) {}
The first version is defining f1 to work on rvalues. The second version, on the other hand, is a template function accepting a universal (or in some references, forwarding) reference as its argument.
To understand the mechanics of std::forward, you should call f2 with different arguments, as in:
#include <iostream>
template <class T> void f2(T &&a) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
int main() {
int a{5};
f2(5);
f2(a);
return 0;
}
When you compile the code, say with g++, you get the following output from your program:
./a.out
void f2(T &&) [T = int]
void f2(T &&) [T = int &]
As you can see, in the first call T is deduced to be int, whereas in the second call it is deduced to be int &. Due to the reference collapsing rules, as already mentioned in the comments to your question, T && will give you T whereas T& && will give you T&. In short, you observing T&& as the return type does not mean that the functions return an rvalue reference. In fact, in the template functions, && is like an identity operator. When combined with T&, it will give you T&; otherwise, it will give you T.
There is a really nice talk at CppCon2016 by Arthur O'Dwyer on this topic. Maybe you can have a look at it to understand the template type deduction rules, which will help you in clarifying the std::forward behaviour.
I thought I'd try to explain the general case in as simple a way as I can (without omitting any details) as I was confused by std::forward for the longest time.
Let [base-type] refer to the unreferenced part of the parameter type (which can have other type descriptors like const but no &'s). These are all the official reference collapsing rules for free (i.e. undetermined) template parameters. The far left column represents the type of T. The far right column represents the corresponding type of T& or T&& (i.e. type of arg in the template).
Type of T Type of arg
. | |
. v v
1 [base-type] + & => [base-type]&
2 [base-type] + && => [base-type]&&
3 [base-type]& + & => [base-type]&
4 [base-type]& + && => [base-type]&
5 [base-type]&& + & => [base-type]&
6 [base-type]&& + && => [base-type]&&
When arg is specified as type T&&, only rules 2, 4, or 6 apply for type deduction.
If the passed arg has the form [base-type]&&, only rules 2 and 6 apply. If rule 2 is used, [base-type] will be substituted for T. If rule 6 is used [base-type]&& will be substituted for T. These cases are practically identical as the return type, T&&, will be [base-type]&& in both cases.
Rule 2: T + && = [base-type] + && = [base-type]&&
Rule 6: T + && = [base-type]&& + && = [base-type]&&
From tests I've done, the compiler always choses T = [base-type] over [base-type]& or [base-type]&&, so long as the former is a valid substitution. Therefore T is always deduced as [base-type] when an argument (arg) of the form [base-type]&& is passed to the template function.
If the passed arg has the form [base-type]&, only rule 4 applies and [base-type]& must be substituted for T. There is only one possibility this time. The return type is thus...
Rule 4: T + && = [base-type]& + && = [base-type]&
The bottom line is this:
In every case the type returned from std::forward<T>(arg) is the
collapsed version of T&&, which is exactly the same as the type of
arg.
Note that this only works as long as arg is specified as being of type T&&. Specifying arg as T& or T doesn't produce the desired universal forwarding, so you shouldn't ever do it. arg T&& is the only universal forwarding argument specification.
I wrote a small tutorial program that can be run online here. It demonstrates that T&& can represent a reference to either an l-value and an r-value. It also shows that T& and T do not work.
passing l-values and r-values with universal forwarding.
The definition of xvalue is as follows:
— An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]
Will we ever fall into where we practically need to use a function whose return type is an rvalue reference, which is an xvalue?
const int && Foo()
{
// ...
}
Move semantics take an rvalue reference as a parameter, not a return value. So I don't think that's the case.
Returning rvalue references can be of use for functions that already take rvalues as parameters. A simple example:
struct X {
X() = default;
X(X&& other) { std::cout << "move ctor\n"; }
X(X const&) = delete;
void log(std::string const& s){ std::cout << "log: " << s << "\n"; }
};
void sink(X&& x) {
x.log("sink");
}
X&& passOn(X&& in) {
in.log("pass");
return std::move(in);
}
X moveOn(X&& in) {
in.log("move");
return std::move(in);
}
int main() {
sink(passOn(X()));
std::cout << "===============================\n";
sink(moveOn(X()));
}
Live demo →
The second function will call the move constructor to create the returned object, while the first will pass on the reference it already got. This is more useful if we don't return the original reference but instead a reference to a part of the referred object, e.g.
template<class T>
T&& getHead(std::vector<T>&& input) {
return std::move(input.front());
}
That's exactly what std::move is — the result of std::move execution is an xvalue. Other than that it is hard to tell since in the main returning a reference from the function is a bad thing most of the time. But maybe someone will come up with another clever usage of such a function.
Will we ever fall into where we practically need to use a function whose return type is an rvalue reference, which is an xvalue?
It used in container classes, for instance tuple has a get overload that looks like this:
template< std::size_t I, class... Types >
typename std::tuple_element<I, tuple<Types...> >::type&&
get( tuple<Types...>&& t );
I assume that std::optional and std::variant in C++17 will both have a similar overloads.
Granted, the only point is to avoid to type std::move in some very specific situations, like:
auto x = std::get<1>( f() );
Where f returns a tuple by value.
In VS2010 std::forward is defined as such:
template<class _Ty> inline
_Ty&& forward(typename identity<_Ty>::type& _Arg)
{ // forward _Arg, given explicitly specified type parameter
return ((_Ty&&)_Arg);
}
identity appears to be used solely to disable template argument deduction. What's the point of purposefully disabling it in this case?
If you pass an rvalue reference to an object of type X to a template function that takes type T&& as its parameter, template argument deduction deduces T to be X. Therefore, the parameter has type X&&. If the function argument is an lvalue or const lvalue, the compiler deduces its type to be an lvalue reference or const lvalue reference of that type.
If std::forward used template argument deduction:
Since objects with names are lvalues the only time std::forward would correctly cast to T&& would be when the input argument was an unnamed rvalue (like 7 or func()). In the case of perfect forwarding the arg you pass to std::forward is an lvalue because it has a name. std::forward's type would be deduced as an lvalue reference or const lvalue reference. Reference collapsing rules would cause the T&& in static_cast<T&&>(arg) in std::forward to always resolve as an lvalue reference or const lvalue reference.
Example:
template<typename T>
T&& forward_with_deduction(T&& obj)
{
return static_cast<T&&>(obj);
}
void test(int&){}
void test(const int&){}
void test(int&&){}
template<typename T>
void perfect_forwarder(T&& obj)
{
test(forward_with_deduction(obj));
}
int main()
{
int x;
const int& y(x);
int&& z = std::move(x);
test(forward_with_deduction(7)); // 7 is an int&&, correctly calls test(int&&)
test(forward_with_deduction(z)); // z is treated as an int&, calls test(int&)
// All the below call test(int&) or test(const int&) because in perfect_forwarder 'obj' is treated as
// an int& or const int& (because it is named) so T in forward_with_deduction is deduced as int&
// or const int&. The T&& in static_cast<T&&>(obj) then collapses to int& or const int& - which is not what
// we want in the bottom two cases.
perfect_forwarder(x);
perfect_forwarder(y);
perfect_forwarder(std::move(x));
perfect_forwarder(std::move(y));
}
Because std::forward(expr) is not useful. The only thing it can do is a no-op, i.e. perfectly-forward its argument and act like an identity function. The alternative would be that it's the same as std::move, but we already have that. In other words, assuming it were possible, in
template<typename Arg>
void generic_program(Arg&& arg)
{
std::forward(arg);
}
std::forward(arg) is semantically equivalent to arg. On the other hand, std::forward<Arg>(arg) is not a no-op in the general case.
So by forbidding std::forward(arg) it helps catch programmer errors and we lose nothing since any possible use of std::forward(arg) are trivially replaced by arg.
I think you'd understand things better if we focus on what exactly std::forward<Arg>(arg) does, rather than what std::forward(arg) would do (since it's an uninteresting no-op). Let's try to write a no-op function template that perfectly forwards its argument.
template<typename NoopArg>
NoopArg&& noop(NoopArg&& arg)
{ return arg; }
This naive first attempt isn't quite valid. If we call noop(0) then NoopArg is deduced as int. This means that the return type is int&& and we can't bind such an rvalue reference from the expression arg, which is an lvalue (it's the name of a parameter). If we then attempt:
template<typename NoopArg>
NoopArg&& noop(NoopArg&& arg)
{ return std::move(arg); }
then int i = 0; noop(i); fails. This time, NoopArg is deduced as int& (reference collapsing rules guarantees that int& && collapses to int&), hence the return type is int&, and this time we can't bind such an lvalue reference from the expression std::move(arg) which is an xvalue.
In the context of a perfect-forwarding function like noop, sometimes we want to move, but other times we don't. The rule to know whether we should move depends on Arg: if it's not an lvalue reference type, it means noop was passed an rvalue. If it is an lvalue reference type, it means noop was passed an lvalue. So in std::forward<NoopArg>(arg), NoopArg is a necessary argument to std::forward in order for the function template to do the right thing. Without it, there's not enough information. This NoopArg is not the same type as what the T parameter of std::forward would be deduced in the general case.
Short answer:
Because for std::forward to work as intended(, i.e. to faitfully pass the original type info), it is meant to be used INSIDE TEMPLATE CONTEXT, and it must use the deduced type param from the enclosing template context, instead of deducing the type param by itself(, since only the enclosing templates have the chance to deduce the true type info, this will be explained in the details), hence the type param must be provided.
Though using std::forward inside non-template context is possible, it is pointless(, will be explained in the details).
And if anyone dares to try implementing std::forward to allow type deducing, he/she is doomed to fail painfully.
Details:
Example:
template <typename T>
auto someFunc(T&& arg){ doSomething(); call_other_func(std::forward<T>(para)); }
Observer that arg is declared as T&&,( it is the key to deduce the true type passed, and) it is not a rvalue reference, though it has the same syntax, it is called an universal reference (Terminology coined by Scott Meyers), because T is a generic type, (likewise, in string s; auto && ss = s; ss is not a rvalue reference).
Thanks to universal reference, some type deduce magic happens when someFunc is being instantiated, specifically as following:
If an rvalue object, which has the type _T or _T &, is passed to someFunc, T will be deduced as _T &(, yeah, even if the type of X is just _T, please read Meyers' artical);
If an rvalue of type _T && is passed to someFunc,T will be deduced as _T &&
Now, you can replace T with the true type in above code:
When lvalue obj is passed:
auto someFunc(_T & && arg){ doSomething(); call_other_func(std::forward<_T &>(arg)); }
And after applying reference collapse rule(, pls read Meyers' artical), we get:
auto someFunc(_T & arg){ doSomething(); call_other_func(std::forward<_T &>(arg)); }
When rvalue obj is passed:
auto someFunc(_T && && arg){ doSomething(); call_other_func(std::forward<_T &&>(arg)); }
And after applying reference collapse rule(, pls read Meyers' artical), we get:
auto someFunc(_T && arg){ doSomething(); call_other_func(std::forward<_T &&>(arg)); }
Now, you can guess what std::forwrd does eseentially is just static_cast<T>(para)(, in fact, in clang 11's implementation it is static_cast<T &&>(para), which is the same after applying reference collapsing rule). Everything works out fine.
But if you think about let std::fowrd deducing the type param by itself, you'll quickly find out that inside someFunc, std::forward literally IS NOT ABLE TO deduce the original type of arg.
If you try to make the compiler do it, it will never be deduced as _T &&(, yeah, even when arg is bind to an _T &&, it is still an lvaule obj inside someFunc, hence can only be deduceed as _T or _T &.... you really should read Meyers' artical).
Last, why should you only use std::forward inside templates? Because in non-templates context, you know exactly what type of obj you have. So, if you have an lvalue bind to an rvalue reference, and you need to pass it as an lvaule to another function, just pass it, or if you need to pass it as rvalue, just do std::move. You simply DON'T NEED std::forward inside non-template context.
I have the code
void prints_one()
{ cout << "one" << endl; }
int main(int argc, char *argv[])
{
std::function<void()> foo;
foo = prints_one;
foo();
return 0;
}
It works as expected; it prints "one". What I don't know is which assignment operator prototype is being invoked in the assignment and how. Looking at cpp reference, it looks like it probably is this function
template <class Fn> function& operator= (Fn&& fn);
But if that is the prototype being called, I don't understand how a function can bind to a rvalue reference. Thanks!
Update: Thanks all, I'll read up on universal references. In regards to 40two's answer; this code prints that it is an rvalue reference:
template<class Fn>
class Foo {
public:
Foo() {}
Foo& operator=(Fn&& x)
{
std::cout << std::boolalpha << std::is_rvalue_reference<decltype(x)>::value << std::endl;
}
};
void prints_one()
{
cout << "one" << endl;
}
int main(int argc, char *argv[])
{
Foo<void()> bar;
bar = prints_one;
}
This prints true
The C++ standard is meant to be confusing! Otherwise, people like me couldn't play smart just because they were watching while the C++ standard got developed. To this end it was decided that the && notation applied to types shall mean two entirely different although also confusingly related things:
When applied in a non-deduced context, i.e., with a known type T, the notation T&& means that an rvalue reference is being declared.
When applied in a deduced context, e.g., in auto&& x = ... or in function template template <typename T> void f(T&& x) the notation is to mean: determine the type and "referenceness" of the argument. That the deduced type T be of different kinds depending on what was passed as argument:
If a non-const lvalue of type X is being passed, the type T will be deduced as X&, and T&& becomes X& as a reference and an rvalue reference collapse into a reference.
If a const lvalue of type X is being passed, the type T will be deduced as X const&, and T&& becomes X const&, due to the same reference collapsing.
If an rvalue is being passed, the type T will be deduced as X, and T&& becomes X&& as there is nothing to collapse.
The motivations driving the rules on argument deduction is perfect forwarding: the argument should be forwarded to the next layer ideally look like the same kind of type as the argument which got deduced in the first places.
With this in mind, the assignment operator of std::function<...> being called is indeed
template <typename F>
std::function<...>::function(F&& f)
where the argument of the function it just deduced accordingly. In your concrete example the function pointer being passed actually is an rvalue created on the fly by decaying the function type: functions aren't really objects and can't be passed along in C++. To access them other than calling a pointer to the function is being formed. Thus, the argument is an rvalue of type void(*)().
Quoting stuff from here:
T&& isn't always an rvalue reference.
&& in a type declaration sometimes could mean an rvalue reference, but sometimes it means either rvalue reference or lvalue reference.
I'll try to contribute to the above with an example. Consider the following piece of code:
#include <iostream>
#include <type_traits>
using namespace std;
template<class F>
void foo(F&& f)
{
std::cout << std::boolalpha << std::is_rvalue_reference<decltype(f)>::value << std::endl;
}
void prints_one()
{
cout << "one" << endl;
}
int main(int argc, char *argv[])
{
foo(prints_one);
foo([](){ cout << "lambda" << endl; });
return 0;
}
If you run it the output is:
false
true
This mean that although foo takes a T&& as an input parameter, because of the fact that prints_one is an lvalue, input parameter f will be initialized with an lvalue, and consequently it will become an lvalue reference.
On the other hand in the second call of foo we pass a lambda which is really a rvalue reference. As such, the input parameter f is initialized with an rvalue and consequently it becomes an rvalue reference.
Thus, whether input parameter is deduced to an lvalue reference or an rvalue reference depends on input parameter that is passed in foo upon the time it is called.
Update:
Regarding the updated example:
You are defining a template class Foo with an assignment operator Foo<T>::operator=(T&&).
In this case T&& is not a universal reference because there's no type deduction.
It is rather an rvalue reference, hence you are getting true.
This is due to the fact that T is already deducted by the class Foo<T>. Consequently, there can't be type deduction for input parameters of member overloaded operator Foo<T>::operator=(T&&).