I wish to create a wrapper around std::make_pair that takes a single argument and uses that argument to make the first and second members of the pair. Furthermore, I wish to take advantage of move semantics.
Naively, we might write (ignoring return types for clarity),
template <typename T>
void foo(T&& t)
{
std::make_pair(std::forward<T>(t),
std::forward<T>(t));
}
but this is unlikely to do what we want.
What we want is:
In the case where foo is called with a (const) lvalue reference argument, we should pass that (const) reference on to std::make_pair unmodified for both arguments.
In the case where foo is called with an rvalue reference argument, we should duplicate the referenced object, then call std::make_pair with the original rvalue reference as well as an rvalue reference to the newly created object.
What I've come up with so far is:
template <typename T>
T forward_or_duplicate(T t)
{
return t;
}
template <typename T>
void foo(T&& t)
{
std::make_pair(std::forward<T>(t),
forward_or_duplicate<T>(t));
}
But I'm reasonably sure it's wrong.
So, questions:
Does this work? I suspect not in that if foo() is called with an rvalue reference then T's move constructor (if it exists) will be called when constructing the T passed by value to forward_or_duplicate(), thus destroying t.
Even if it does work, is it optimal? Again, I suspect not in that T's copy constructor will be called when returning t from forward_or_duplicate().
This seems like a common problem. Is there an idiomatic solution?
So, questions:
Does this work? I suspect not in that if foo() is called with an rvalue reference then T's move constructor (if it exists) will be
called when constructing the T passed by value to
forward_or_duplicate(), thus destroying t.
No, t in foo is an lvalue, so constructing the T passed by value to
forward_or_duplicate() from t calls the copy constructor.
Even if it does work, is it optimal? Again, I suspect not in that T's copy constructor will be called when returning t from
forward_or_duplicate().
No, t is a function parameter, so the return implicitly moves, and doesn't copy.
That said, this version will be more efficient and safer:
template <typename T>
T forward_or_duplicate(std::remove_reference_t<T>& t)
{
return t;
}
If T is an lvalue reference, this results in the same signature as before. If T is not a reference, this saves you a move. Also, it puts T into a non-deduced context, so that you can't forget to specify it.
Your exact code works. Slight variations of it (ie, not calling make_pair but some other function) result in unspecified results. Even if it appears to work, subtle changes far from this line of code (which are locally correct) can break it.
Your solution isn't optimal, because it can copy a T twice, even when it works, when it only needs to copy it once.
This is by far the easiest solution. It doesn't fix the subtle breaks caused by code elsewhere changing, but if you are really calling make_pair that is not a concern:
template <typename T>
void foo(T&& t) {
std::make_pair(std::forward<T>(t),
static_cast<T>(t));
}
static_cast<T>(t) for a deduced type T&& is a noop if T&& is an lvalue, and a copy if T&& is an rvalue.
Of course, static_cast<T&&>(t) can also be used in place of std::forward<T>(t), but people don't do that either.
I often do this:
template <typename T>
void foo(T&& t) {
T t2 = t;
std::make_pair(std::forward<T>(t),
std::forward<T>(t2));
}
but that blocks a theoretical elision opportunity (which does not occur here).
In general, calling std::forward<T>(t) on the same function call as static_cast<T>(t) or any equivalent copy-or-forward function is a bad idea. The order in which arguments are evaluated is not specified, so if the argument consuming std::forward<T>(t) is not of type T&&, and its constructor sees the rvalue T and moves state out of it, the static_cast<T>(t) could evaluate after the state of t has been ripped out.
This does not happen here:
template <typename T>
void foo(T&& t) {
T t2 = t;
std::make_pair(std::forward<T>(t),
std::forward<T>(t2));
}
because we move the copy-or-forward to a different line, where we initialize t2.
While T t2=t; looks like it always copies, if T&& is an lvalue reference, T is also an lvalue reference, and int& t2 = t; doesn't copy.
Related
The standard signature of std::forward is:
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&) noexcept;
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;
Because the parameter type isn't T directly, we should specify the template argument when using std::forward:
template<typename... Args>
void foo(Args&&... args)
{
bar(std::forward<Args>(args)...);
}
However sometimes the template argument is not as simple as Args. auto&& is a case:
auto&& vec = foo();
bar(std::forward<decltype(vec)>(vec));
You can also imagine more complicated template argument types for std::forward. Anyway, intuitively std::forward should know what T is but it actually don't.
So my idea is to omit <Args> and <decltype(vec)> no matter how simple they are. Here is my implementation:
#include <type_traits>
template<typename T>
std::add_rvalue_reference_t<std::enable_if_t<!std::is_lvalue_reference<T>::value, T>>
my_forward(T&& obj)
{
return std::move(obj);
}
template<typename T>
T& my_forward(T& obj)
{
return obj;
}
int main()
{
my_forward(1); // my_forward(int&&)
int i = 2;
my_forward(i); // my_forward(int&)
const int j = 3;
my_forward(j); // my_forward(const int&)
}
When obj is rvalue reference, for example int&&, the first overload is selected because T is int, whose is_lvalue_reference is false;
When obj is lvalue reference, for example const int&, the second overload is selected because T is const int& and the first is SFINAE-ed out.
If my implementation is feasible, why is std::forward still requiring <T>? (So mine must be infeasible.)
If not, what's wrong? And still the question, is it possible to omit template parameter in std::forward?
The problematic case is when you pass something of rvalue reference type but which does not belong to an rvalue value category:
int && ir{::std::move(i)};
my_forward(ir); // my_forward(int&)
Passing type to std::forward will ensure that arguments of rvalue reference types will be moved further as rvalues.
The answer by user7860670 gives you an example for the case where this breaks down. Here is the reason why the explicit template parameter is always needed there.
By looking at the value of the forwarding reference you can no longer reliably determine through overload resolution whether it is safe to move from. When passing an lvalue reference parameter as an argument to a nested function call it will be treated as an lvalue. In particular, it will not bind as an rvalue argument, that would require an explicit std::move again. This curious asymmetry is what breaks implicit forwarding.
The only way to decide whether the argument should be moved onwards is by inspecting its original type. But the called function cannot do so implicitly, which is why we must pass the deduced type explicitly as a template parameter. Only by inspecting that type directly can we determine whether we do or do not want to move for that argument.
I'm used to pass lambda functions (and other callables) to template functions -- and use them -- as follows
template <typename F>
auto foo (F && f)
{
// ...
auto x = std::forward<F>(f)(/* some arguments */);
// ...
}
I mean: I'm used to pass them through a forwarding reference and call them passing through std::forward.
Another Stack Overflow user argue (see comments to this answer) that this, calling the functional two or more time, it's dangerous because it's semantically invalid and potentially dangerous (and maybe also Undefined Behavior) when the function is called with a r-value reference.
I've partially misunderstand what he means (my fault) but the remaining doubt is if the following bar() function (with an indubitable multiple std::forward over the same object) it's correct code or it's (maybe only potentially) dangerous.
template <typename F>
auto bar (F && f)
{
using A = typename decltype(std::function{std::forward<F>(f)})::result_type;
std::vector<A> vect;
for ( auto i { 0u }; i < 10u ; ++i )
vect.push_back(std::forward<F>(f)());
return vect;
}
Forward is just a conditional move.
Therefore, to forward the same thing multiple times is, generally speaking, as dangerous as moving from it multiple times.
Unevaluated forwards don't move anything, so those don't count.
Routing through std::function adds a wrinkle: that deduction only works on function pointers and on function objects with a single function call operator that is not && qualified. For these, rvalue and lvalue invocation are always equivalent if both compiles.
I'd say the general rule applies in this case. You're not supposed to do anything with a variable after it was moved/forwarded from, except maybe assigning to it.
Thus...
How do correctly use a callable passed through forwarding reference?
Only forward if you're sure it won't be called again (i.e. on last call, if at all).
If it's never called more than once, there is no reason to not forward.
As for why your snippet could be dangerous, consider following functor:
template <typename T>
struct foo
{
T value;
const T &operator()() const & {return value;}
T &&operator()() && {return std::move(value);}
};
As an optimization, operator() when called on an rvalue allows caller to move from value.
Now, your template wouldn't compile if given this functor (because, as T.C. said, std::function wouldn't be able to determine return type in this case).
But if we changed it a bit:
template <typename A, typename F>
auto bar (F && f)
{
std::vector<A> vect;
for ( auto i { 0u }; i < 10u ; ++i )
vect.push_back(std::forward<F>(f)());
return vect;
}
then it would break spectacularly when given this functor.
If you're either going to just forward the callable to another place or simply call the callable exactly once, I would argue that using std::forward is the correct thing to do in general. As explained here, this will sort of preserve the value category of the callable and allow the "correct" version of a potentially overloaded function call operator to be called.
The problem in the original thread was that the callable was being called in a loop, thus potentially invoked more than once. The concrete example from the other thread was
template <typename F>
auto map(F&& f) const
{
using output_element_type = decltype(f(std::declval<T>()));
auto sequence = std::make_unique<Sequence<output_element_type>>();
for (const T& element : *this)
sequence->push(f(element));
return sequence;
}
Here, I believe that calling std::forward<F>(f)(element) instead of f(element), i.e.,
template <typename F>
auto map(F&& f) const
{
using output_element_type = decltype(std::forward<F>(f)(std::declval<T>()));
auto sequence = std::make_unique<Sequence<output_element_type>>();
for (const T& element : *this)
sequence->push(std::forward<F>(f)(element));
return sequence;
}
would be potentially problematic. As far as my understanding goes, the defining characteristic of an rvalue is that it cannot explicitly be referred to. In particular, there is naturally no way for the same prvalue to be used in an expression more than once (at least I can't think of one). Furthermore, as far as my understanding goes, if you're using std::move or std::forward or whatever other way to obtain an xvalue, even on the same original object, the result will be a new xvalue every time. Thus, there also cannot possibly be a way to refer to the same xvalue more than once. Since the same rvalue cannot be used more than once, I would argue (see also comments underneath this answer) that it would generally be a valid thing for an overloaded function call operator to do something that can only be done once in case the call happens on an rvalue, for example:
class MyFancyCallable
{
public:
void operator () & { /* do some stuff */ }
void operator () && { /* do some stuff in a special way that can only be done once */ }
};
The implementation of MyFancyCallable may assume that a call that would pick the &&-qualified version cannot possibly happen more than once (on the given object). Thus, I would consider forwarding the same callable into more than one call to be semantically broken.
Of course, technically, there is no universal definition of what it actually means to forward or move an object. In the end, it's really up to the implementation of the particular types involved to assign meaning there. Thus, you may simply specify as part of your interface that potential callables passed to your algorithm must be able to deal with being called multiple times on an rvalue that refers to the same object. However, doing so pretty much goes against all the conventions for how the rvalue reference mechanism is generally used in C++, and I don't really see what there possibly would be to be gained from doing this…
I have seen multiple instances of code where function parameter pack is declared using the && notation, as shown below, but I cannot see any advantage to using this notation.
template<typename... Args>
void Function(Args... args)
{
}
template<typename... Args>
void Function(Args&&... args)
{
}
My first thought was that the && form will be used exclusively for r-value objects, but this test proved that wrong:
struct Object
{
// Added bodies so I see what is being called via a step-into
Object() {}
Object(const Object&) {}
Object(Object&&) noexcept {}
Object& operator=(const Object&) { return *this; }
Object& operator=(Object&&) noexcept { return *this; }
};
Object GetObject() { Object o; return o; }
Object obj;
Function(GetObject());
Function(GetObject());
Here, VS 2017 complains that both forms of the function are viable candidates for the call.
Can someone explain what the difference is between these two, and what advantages one may have over the other please?
They are forwarding references in the parameter pack form. As for template parameter deduction, they can match any arguments, but the template parameter will be deduced differently comparing to the ordinary template parameter.
The major advantage of forwarding reference is that the lvalue/rvalue information will be preserved if used with std::forward. Thus they are used to "forward" something.
For example,
void real_foo(A const &a);
void real_foo(A &&a);
template<class... Args>
void foo_proxy_ordinary(Args... args) { real_foo(args...); }
template<class... Args>
void foo_proxy_perfect(Args&&... args) { real_foo(std::forward<Args>(args)...); }
The ordinary version will always call real_foo(A const &) version, because inside foo_proxy, args are always lvalue.
However, the perfect version will select real_foo(A&&) if the arguments passed in are indeed rvalues.
Combining forwarding reference with parameter pack, one can write easily generic proxy functions without performance loss in terms of lvalue/rvalue.
T&& when used in the context of
template<typename T>
void f(T&& t);
is called a forwarding reference sometimes also called a universal reference.
Main advantage of a forwarding reference is that combined with std::forward it enables achieving a so-called perfect forwarding: function template passing its arguments to another function as they are (lvalue as lvalue, rvalue as rvalue).
Now it is possible to create higher-order functions that take other functions as arguments or return them, or superior function-wrappers (e.g., std::make_shared), and do other cool things.
Here is some material that explains it much better and in more detail than I possibly can:
Perfect forwarding and universal references in C++
Rvalue References and Perfect Forwarding in C++0x
Forwarding references proposal
SO: Advantages of using forward
SO: Perfect forwarding - what's it all about?
Can someone explain what the difference is between these two, and what advantages one may have over the other please?
The difference is same for parameter packs as it is for individual parameters. Args declares an "object parameter" (pass by value) and Args&& declares a reference parameter (pass by reference).
Passing by reference allows one to avoid copying the argument when that is unnecessary. It also allows modifying the referred argument if the reference is non-const, which includes the possibility of moving from that object.
Passing by value makes it clear to the caller that the passed object will neither be modified, nor be referred to as a result of calling the function.
My first thought was that the && form will be used exclusively for r-value objects
As your test demonstrates, that is indeed an incorrect assumption. When Args is a deduced type i.e. auto or a template argument, Args&& can indeed be either an l-value reference or an r-value reference. Which one it is depends on what Args is deduced to be. This demonstrates the reference collapsing rules concisely:
typedef int& lref;
typedef int&& rref;
int n;
lref& r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int& note this case in particular
rref& r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&
Using such reference allows forwarding i.e. re-binding into a new lvalue reference (when possible) or moving from the object (when possible) or copying (when neither of the previous is possible).
Because of this, Args&& is called a forwarding reference (or a universal reference) when Args is a deduced type.
I was looking at the std::function implementation and its call operator()
template<typename Ret, typename... ArgTypes>
Ret function< Ret (ArgTypes...)>::operator()(ArgTypes...args) const
{
// some stuff
return invoker(functor, std::forward<ArgTypes>(args)...);
}
I was particularly wondering, why it uses std::forward here? Does this anything have to do with perfect forwarding?
Because perfect forwarding could only be done if operator() is a template with a variadic template declaration template<typename... Args> (which it is not, the declaration is a partial specialization of std::function).
What is the intention of using std::forward here? I am confused :-)?
You are correct that this is not your typical "perfect forwarding" scenario. A brief example can help illustrate the motivation. Assume a type A with instrumented constructors and destructor:
#include "A.h"
#include <functional>
#include <iostream>
int
main()
{
A a1{1};
A a2{2};
std::function<void(A, A&)> f{[](A x, A& y){}};
f(a1, a2);
}
This will output:
A(int state): 1
A(int state): 2
A(A const& a): 1
A(A&& a): 1
~A(1)
~A(-1)
~A(2)
~A(1)
Explanation:
a1 and a2 are constructed on the stack. Then when passed into the function invoker, a1 is first copied to bind to the first by-value parameter, and then std::forward<A> is called on a1 which moves it from the by-value parameter into the lambda.
In contrast, a2 need not be copied to bind to the function A& parameter, and then std::forward<A&>(a2) is called, which forwards a2 as an lvalue instead of rvalue, and this binds to the A& parameter of the lambda.
Then things get destructed. The ~A(-1) indicates the destruction of an A in a move-constructed-from state with this instrumented A.
In summary, even though ArgTypes isn't deduced as in the usual perfect forwarding idiom, we still want to forward by-value ArgTypes as rvalues, and by-reference ArgTypes as lvalues. So std::forward just happens to do exactly what we want here.
I think you are confused by many things here.
First, perfect forwarding has nothing to do with variadic templates. You could create a wrapper class that has a function that takes one argument and forward it to the wrapped object :
template<typename T>
struct Wrapper {
template<typename Arg>
decltype(auto) test(Arg&& arg) {
return t.test(std::forward<Arg>(arg));
}
T t;
};
Notice the use of perfect forwarding here without any variadic templates. If t.test would require a move only type as parameter, it would not be possible to call it without the forward<Arg>(arg).
The second thing happening here is the parameter not being followed by &&. Adding && to ArgTypes would be a mistake and would make some cases fail to compile. Consider this simple case :
std::function<void(int)> f;
int i = 0;
f(i);
That would fail to compile. If you add && to ArgTypes, every parameters that are not reference (eg. int) would become an rvalue reference on the call operator (in our case, int&&). Since all parameter types are already qualified correctly in the std::function argument list, what you want to recieve in the call operator is exactly those types, not transformed.
The why you need std::forward if you don't use &&? Because even though you don't need to infer value categories, you still need to not copy every arguments to the contained function. If one of the std::function's parameter is int&, you don't want to move it. But if one of the parameter is std::unique_ptr<int>, you must move it! And this is exactly what std::forward is for. Moving only what should be moved.
std::forward just appends rvalue reference to the type, so taking reference collapsing rules into account, it effectively passes reference arguments as-is and moves the object arguments.
1)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg arg)
{
return shared_ptr<T>(new T(arg));
}
2)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg& arg)
{
return shared_ptr<T>(new T(arg));
}
3)
template<typename T, typename Arg>
shared_ptr<T> factory(Arg const & arg)
{
return shared_ptr<T>(new T(arg));
}
*)Why is number 3 is preferred than number 1 and number 2?
*)if factory(41) is called, why is called on rvalue?
*)# define BOOST_ASIO_MOVE_ARG(type) type&&. what's && in this case?
Actually, approach #3 is not better than 1 and 2. It all depends on T's constructor(s).
A somewhat standard way to do this is using rvalue references, which are the && you mention.
So, a better factory would be something like this: (which is what perfect forwarding actually is)
template<typename T, typename Arg>
std::shared_ptr<T> factory(Arg && arg)
{
return std::shared_ptr<T>(new T (std::forward<Arg>(arg)));
}
Due to reference collapsing rules of C++, this function can take (and forward) both rvalue refences and lvalue references, i.e., if you pass it an rvalue, it will forward that rvalue faithfully to T's constructor, and if you give it an lvalue, the two kinds of reference (Something & && arg) will collapse and the lvalue reference will be forwarded to the underlying constructor.
I think I covered your questions here, but to answer explicitly,
#3 is not the preferred method here. Even before C++11, you probably wanted both const& and & overloads of the factory function. The const& version is nicer and it will actually accept temporary values, but if your underlying T type has a constructor that accepts a non-const ref, then you would get a compile error because a const& is not implicitly casted to a &.
Because you can't take the address of the literal 41 (this is not 100% technically correct, but I think it's OK to think of lvalues and rvalues this way.)
It signifies an rvalue reference. You need to read about that; an explanation will not fit here, and there are already several great ones just a Google search away!
UPDATE: As mentioned tangentially in the comments, using make_shared can be better than constructing a shared_ptr with a just-newed pointer. make_shared might achieve better efficiency by allocating the control block (which includes the reference count) along with the object itself, which would provide better cache locality when accessing the reference counter and the object, and also may save one memory allocation. Even if the implementation has none of the optimizations, it won't be any worse than the above version. So use std::make_shared whenever possible! And this would be how you do it here:
template<typename T, typename Arg>
std::shared_ptr<T> factory (Arg && arg)
{
return std::make_shared<T>(std::forward<Arg>(arg));
}
*)Why is number 3 is preferred than number 1 and number 2?
(1) won't work if Arg is non-copyable.
(2) won't allow you to pass an rvalue, as in factory<int>(42);
Note that none of the three examples involve perfect forwarding. I'm not sure what the subject of your question refers to.
*)if factory(41) is called, why is called on rvalue?
I'm not sure I understand the question. 41 is an rvalue by definition.
*)# define BOOST_ASIO_MOVE_ARG(type) type&&. what's && in this case?
type&& is an rvalue reference to type.