There two declarations for std::vector::push_back.I understand rvalue and lvalue to some degree. As far as i know, almost all types(T&、T&&、T) could be converted to const T&, so which one does the compiler choose when different types of object passed to std::vector::push?
I am a novice in C++.Though i thought over and over, i still could not get the idea.It would be better if you could give me some simple examples to make it clear.I would be greateful to have some help with this question.
As per the documatation(http://www.cplusplus.com/reference/vector/vector/push_back/), which says that:
void push_back (const value_type& val);
void push_back (value_type&& val);
Adds a new element at the end of the vector, after its current
last element. The content of val is copied (or moved) to the new
element.
Lvalues cannot be bind to rvalue referneces, which implies, that when you invoke std::vector<T>::push_back with an lvalue argument, the only viable overload is that with const T& parameter.
Rvalues can be bind to both rvalue references and const lvalue references. Therefore, both overloads are applicable. But according to the C++ overload rules, the overload with rvalue reference parameter T&& will be selected.
You can easily try this by yourself:
void f(const int&) { std::cout << "L"; }
void f(int&&) { std::cout << "R"; }
int main()
{
int i = 0;
f(i); // prints "L"
f(0); // prints "R"
}
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.
In Item 41, Scott Meyers writes the following two classes:
class Widget {
public:
void addName(const std::string& newName) // take lvalue;
{ names.push_back(newName); } // copy it
void addName(std::string&& newName) // take rvalue;
{ names.push_back(std::move(newName)); } // move it; ...
private:
std::vector<std::string> names;
};
class Widget {
public:
template<typename T> // take lvalues
void addName(T&& newName) // and rvalues;
{ // copy lvalues,
names.push_back(std::forward<T>(newName)); } // move rvalues;
} // ...
private:
std::vector<std::string> names;
};
What's written in the comments is correct, even if it doesn't mean at all that the two solutions are equivalent, and some of the differences are indeed discussed in the book.
In the errata, however, the author comments another difference not discussed in the book:
Another behavioral difference between (1) overloading for lvalues and rvalues and (2) a template taking a universal reference (uref) is that the lvalue overload declares its parameter const, while the uref approach doesn't. This means that functions invoked on the lvalue overload's parameter will always be the const versions, while functions invoked on the uref version's parameter will be the const versions only if the argument passed in is const. In other words, non-const lvalue arguments may yield different behavior in the overloading design vis-a-vis the uref design.
But I'm not sure I understand it.
Actually, writing this question I've probably understood, but I'm not writing an answer as I'm still not sure.
Probably the author is saying that when a non-const lvalue is passed to addName, newName is const in the first code, and non-const in the second code, which means that if newName was passed to another function (or a member function was called on it), than that function would be required to take a const parameter (or be a const member function).
Have I interpreted correctly?
However, I don't see how this makes a difference in the specific example, since no member function is called on newName, nor it is passed to a function which has different overloads for const and non-const parameters (not exactly: std::vector<T>::push_back has two overloads for const T& arguments and T&& arguments`, but an lvalue would still bind only to the former overload...).
In the second scenario, when a const std::string lvalue is passed to the template
template<typename T>
void addName(T&& newName)
{ names.push_back(std::forward<T>(newName)); }
the instantiation results in the following (where I've removed the std::forward call as it is, in practice, a no-op)
void addName(const std::string& newName)
{ names.push_back(newName); }
whereas if a std::string lvalue is passed, then the resulting instance of addName is
void addName(std::string& newName)
{ names.push_back(newName); }
which means that the non-const version of std::vector<>::push_back is called.
In the first scenario, when a std::string lvalue is passed to addName, the first overload is picked regardless of the const-ness
void addName(const std::string& newName)
{ names.push_back(newName); }
which means that the const overload of std::vector<>::push_back is selected in both cases.
Does std::function support perfect forwarding of arguments? I decided to test it:
struct Widget
{
void operator()(int const&)
{
std::cout << "lvalue";
}
void operator()(int&&)
{
std::cout << "rvalue";
}
};
std::function<void(int)> f{Widget()};
int x = 5;
f(x);
Guess which operator() gets called - the one taking rvalue reference. It seems that it is by design. What is the rationale behind this behavior?
Yes and no. Yes, the arguments are forwarded. But no, overload resolution is not done based on the arguments you provide at the point of the call - it's done based on the template arguments to std::function.
std::function<void(int)> means you have a callable that takes an int, which implicitly is an int rvalue. Calling Widget with an rvalue of type int prefers the int&& overload over the int const& overload, which is why that one is selected. It doesn't matter how you invoke the actual function object, once you selected that you want void(int) - that's the preferred overload.
The actual call operator behaves as if:
struct widget_function {
void operator()(int arg) const {
// ~~~~~ ~~~~
w_(std::forward<int>(arg));
// ~~~
}
Widget w_;
};
The underlined portions come from the signature you provide to std::function. You can see that both f(x) and f(5) end up calling the same operator() from Widget.
In short, std::function<Sig> type erases one single overload that satisfies Sig. It does not type erase an entire overload set. You asked for void(int), so you get void(int) and only void(int).
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.
Potentially related articles:
Overload resolution between object, rvalue reference, const reference
std::begin and R-values
For a STL container C, std::begin(C) and similar access functions including std::data(C) (since C++17) are supposed to have the same behavior of C::begin() and the other corresponding C's methods. However, I am observing some interesting behaviors due to the details of overload resolution involving lvalue/rvalue references and constness.
DataType1 is int* as easily expected. Also, confirmed the by with Boost's type_id_with_cvr. const vector<int> gives int const* No surprise here.
using T = vector<int>;
using DataType1 = decltype(T().data()); // int*
using CT = const T;
using DataType2 = decltype(CT().data()); // int const*
using boost::typeindex::type_id_with_cvr;
cout << type_id_with_cvr<DataType1>() << endl; // prints int*
...
I tried std::data, which can also handle arrays and non-STL containers. But it yields int const*. Similarly, std::begin returns a const iterator, even though T is not const.
using T = vector<int>;
using DataType3 = decltype(std::data(T())); // int const* Why ???
using CT = const T;
using DataType4 = decltype(std::data(CT())); // int const*
Question: What is the reason of this difference? I expected that C.data() and std::data(C) would behave in the same manner.
Some my research: In order to get int* for DataType3, T must be converted to non-const lvalue reference type explicitly. I tried declval, and it was working.
using DataType3 = decltype(std::data(std::declval<T&>())); // int*
std::data provides two overloads:
template <class _Cont> constexpr
auto data(_Cont& __c) -> decltype(__c.data()) { return __c.data(); }
// non-const rvalue reference argument matches to this version.
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }
While resolving overloaded functions for std::data, T(), which is non-const rvalue reference, is matched to the const T& version instead of T& version.
It was not easy to find this specific overload resolution rule in the standard (13.3, over.match). It'd be much clearer if someone could point the exact rules for this issue.
This behaviour is attributed to overload resolution rules. As per standard 8.5.3/p5.2 References [dcl.init.ref], rvalue references bind to const lvalue references. In this example:
std::data(T())
You provide to std::data an rvalue. Thus, due to overload resolution rules the overload:
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }
is a better match. Consequently you get const int*
You can't bind a temporary to a non-const lvalue reference.
The only line that is mildly surprising is using DataType1 = decltype(T().data()); // int*.
...but it is still normal, member functions can be called on temporary objects without being treated as const. This is another non trivial difference between member functions and free functions.
For example, in C++98 (pre-rvalue refs) it was not possible to do std::ofstream("file.txt") << std::string("text") because operator<< is not member and the temporary is treated as const. If operator<< were a member of std::ofstream it would be possible (and may even make sense). (The situation changed later in C++11 with rvalue references but the point is still valid).
Here it is an example:
#include<iostream>
struct A{
void f() const{std::cout << "const member" << std::endl;}
void f(){std::cout << "non const member" << std::endl;}
};
void f(A const&){std::cout << "const function" << std::endl;}
void f(A&){std::cout << "non const function" << std::endl;}
int main(){
A().f(); // prints "non const member"
f(A()); // prints "const function"
}
The behavior is exposed when the object is temporarily constructed and used. In all other cases I can imagine, member f is equivalent to f free function.
(r-value reference qualifications && --for member and function-- can give you a more fine grained control but it was not part of the question.)