So I have a std::map< std::string, boost::any > called OptionsMap and I want to create a function that takes any type (therefore the template) and stores it in the map.
Would the next code work?
template <typename T>
void Set(std::string optionName, T&& optionValue)
{
OptionsMap[optionName] = optionValue;
}
Typically, when a function template accepts a universal reference (i.e. an rvalue reference of a deduced type), you should cast the function parameter with forward so as to obtain a value of the same value category as was provided by the argument in the function call:
template <typename T>
void Set(std::string optionName, T&& optionValue)
{
OptionsMap[optionName] = std::forward<T>(optionValue);
// ^^^^^^^^^^^^^^^
}
The parameter variable optionValue itself is always an lvalue, so without the cast, you would be making copies of what should be moves. If the type is copyable, this will be less efficient than expected; if the type isn't copyable (e.g. unique_ptr), it's worse: your function now accepts an argument that it will subsequently fail to compile with.
Of course. Why wouldn’t it work?
boost::any::operator= accepts an argument of any data type satisfying ValueType.
I would just take by value and move it, for forward-compatibility when boost::any starts supporting move semantics.
template <typename T>
void Set(std::string optionName, T optionValue)
{
OptionsMap[optionName] = std::move(optionValue);
}
Passing an rvalue to this function will move it when it’s movable. That’s guaranteed by the standard.
Related
Lets consider ordinary perfect forwarding:
class Test
{
public:
Test() = default;
Test(Test const&) { std::cout << "copy\n"; }
Test(Test&&) { std::cout << "move\n"; }
};
void test(Test)
{ }
template <typename T>
void f(T&& t)
{
test(std::forward<T>(t));
}
int main()
{
std::cout << "expect: copy\n";
Test t;
f(t);
std::cout << "expect: move\n";
f(Test());
return 0;
}
So far everything is fine. But if we now introduce a function pointer I get the problem that I seem not to be able to declare universal (forwarding) references:
decltype(&f<Test>) ptr = &f<Test>;
// above produces ordinary r-value references
// making below fail already on compilation:
ptr(t);
Template function pointers get problematic as well:
template <typename T>
void(*ptr)(T&& t) = &f<T>; // again resolved to r-value reference
At first, they resolve to pure r-value reference as well, additionally they define a bunch of pointers instead of a single one, making the approach unusable within class scope:
class C
{
template <typename T>
void(*ptr)(T&& t); // fails (of course...)
};
So question now is: Is it possible at all to have indirect perfect forwarding via function pointers?
Admitted, already fearing the answer is 'no' (and currently falling back to l-value references), but still in hope of having overlooked something somewhere...
A forwarding reference is not only a hypothetical concept. It is the name given to a specific kind of rvalue reference which has special deduction rules in template argument deduction.
Specifically according to [temp.deduct.call]/3:
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Except for this special rule (and one other in [temp.deduct.type]), a forwarding reference behaves just like any other rvalue reference.
In particular when providing a template argument for T, there is no template argument deduction happening and substitution is performed directly. In substitution the usual reference collapsing rules are applied.
So f<Test> will yield the function parameter Test&& which is a rvalue reference and f<Test&> will yield the function parameter Test& (collapsed from && being applied to Test&).
These are also the template arguments that would be deduced for a rvalue argument and a lvalue argument in a function call without explicit template arguments. If the reference was not a forwarding reference, then T could never be deduced to a reference and the function parameter would always be a rvalue reference after substitution. The special adjustment of A mentioned in the quote allows T to be deduced to an lvalue reference type, so that the collapsing rules will result in an lvalue reference function parameter as well.
Forwarding references can only exist in templates. A function or specialization of a template cannot have a forwarding reference. They are not somehow a different category of reference from rvalue/lvalue references. They work in templates only by deducing to different types for different value categories of arguments and produce distinct specializations for each value category.
So, since a function pointer must point to a function, not a function template, which of the two specializations for the value category of the argument to choose, has to be decided when taking the function pointer.
If deduction of any kind is expected, then a function pointer cannot offer that. Instead a lambda or functor type should be used which can perform deduction and can choose the function or function template specialization to call based on value category.
The first example uses template argument deduction and reference collapsing rules to compute the correct type. When you explicitly supply the template arguments this disables deduction and the supplied type is substituted directly, resulting in the rvalue reference.
Perfect forwarding requires the template argument to be deduced. A function pointer can only point to a single function, not to a set of overloads, and not to a function template.
You can get a function pointer to either the r-value reference instantiation:
decltype(&f<Test&&>) ptr1 = &f<Test&&>;
ptr1(Test()); // output: move
or the l-value instantiation:
decltype(&f<Test&>) ptr2 = &f<Test&>;
ptr2(t); // output: copy
But not to both at the same time. ptr1 and ptr2 are pointers to functions of different type.
When you want something to hold not just a single function but more than one overload you can use a type with member functions. For example with a lambda expression:
auto fw = [](auto&& t){ test(std::forward<decltype(t)>(t)); };
fw(t); // output: copy
fw(Test()); // output: move
Background of the question is a mis-reading of Scott Meyer's article about forwarding references (called 'universal references' there).
The article gave the impression of such forwarding references existing as a separate type in parallel to ordinary l-value and r-value references. This is not the case, though, instead it is a hypothetical construct to explain special deduction rules that exist if template parameters serve as function parameter types.
As such a type does not exist in consequence it is not possible to declare a function pointer using that type, proving indirect perfect forwarding via function pointer impossible to realise.
Here for example, b is of type int& but f(b) resolves to f(int):
#include <type_traits>
template <typename T>
void f(T arg) {
static_assert(std::is_reference<T>::value); // fails
}
void g() {
int a = 5;
int& b = a;
f(b);
}
I know the standard dictates it - I'm asking, why? What goes wrong (or - becomes 'surprising') if the reference isn't dropped?
There are two things at play here.
What if you, as the author of f, wants to write a template function where the user is required to copy/move a parameter into the function?
In non-template code, that prototype would look like this: void f(SomeType st);. Any caller of f has no choice but to copy/move into the parameter. Whether they have an object, a reference to an object, or something else. st shall be an object separate and distinct from the rest.
Template argument deduction is intended to look like the non-template version where possible. So if you have template<typename T> void f(T t);, and you call it with f(some_object), that should work exactly like the untemplated version.
The other thing at play here is that reference variables are supposed to just be a different name for some object. That is, a and b, should, all things being equal, behave identically. So f(a) ought to do the same thing as f(b).
The rules of template argument deduction favor both of these. It keeps references behaving exactly like the object, and it allows the template function to behave like an equivalently-defined non-template version.
If you want a reference type, you must forgo template argument deduction and specify it directly: f<decltype((b))>(b). Note the use of double-parentheses here; that's important for getting decltype to be a reference.
According to the rules of the language, f(b) is not calling f with an expression of type int&. It is calling f with an lvalue of type int. The same would also occur if you did f(a).
In general, when a reference variable is mentioned by name, it is an expression that is an lvalue of the referenced type. The fact that the variable itself is a reference is not visible to the type system.
Thus, f never deduces T to have reference type. From f's point of view, the argument is never actually a reference.
What goes wrong (or - becomes 'surprising') if the reference isn't dropped?
Consider following function:
template <typename T>
void f(T arg) {
something = std::move(arg);
// ...
f(lvalue); // copies
f(str::move(lvalue)); // moves
The intention is to copy from an lvalue, and move from an rvalue.
If T deduced to a reference, then lvalue argument would be moved from, which is in this case undesirable.
I don't want to copy the argument.
If you're writing the function, then specify a reference parameter using & symbol, and not an object parameter. That way the argument can never be copied into the call (you can still copy inside the function).
If you're calling a function that accepts an object parameter, then pass an rvalue and not an lvalue. That way you will copy only if the move is a copy, and there is no copy elision involved.
Doesn't make sense to me that the template type deduction rules decide for me that I do
You decided to use T. You should instead decide to use T&, T&& or const T& if you want a reference.
Say I have two overloads of a method in a class, set_value which each take a universal reference. I'd like to have one function call the other to avoid code duplication, but I'm not sure which is the correct way to give the universal reference to the other function.
template <typename U>
void set_value(U&& value) {
this->value_ = std::forward<U>(value);
}
template <typename U>
void set_value(U&& value, int overloaded) {
// which is the correct way to use the other `set_value` overload?
set_value(value); // just use value directly?
set_value(std::forward<U>(value)); // or should I forward it?
}
If you want perfect forwarding of value, use std::forward
Even though value might be bound to an rvalue, when you use it by name inside set_value it is an lvalue. Therefore, if you do set_value(value); the universal reference in the one-parameter overload would be bound to an lvalue reference.
We can confirm this by adding doing something like this:
template<class T> class TD; // Allows us to view type deduction results in
// the form of compilation errors.
// (From "Effective Modern C++")
...
template <typename U>
void set_value(U&& value) {
TD<decltype(value)> td;
}
...
int i;
set_value(i, 0);
set_value(3, 0);
When forwarding is used, this will give you a compilation error that will reveal the type of value to be int& for set_value(i, 0);. If you comment out that statement you'll get a compilation error for set_value(3, 0); revealing the type of value to be int&&.
If you remove the forwarding you'll get int& in both cases.
Consider this snippet of code, which uses the common idiom of having a function template construct an instance of a class template specialized on a deduced type, as seen with std::make_unique and std::make_tuple, for example:
template <typename T>
struct foo
{
std::decay_t<T> v_;
foo(T&& v) : v_(std::forward<T>(v)) {}
};
template <typename U>
foo<U> make_foo(U&& v)
{
return { std::forward<U>(v) };
}
In the context of Scott Meyers' "universal references", the argument to
make_foo is a universal reference because its type is U&& where U is
deduced. The argument to the constructor of foo is not a universal reference
because although its type is T&&, T is (in general) not deduced.
But in the case in which the constructor of foo is called by make_foo, it
seems to me that it might make sense to think of the argument to the constructor
of foo as being a universal reference, because T has been deduced by the
function template make_foo. The same reference collapsing rules will apply
so that the type of v is the same in both functions. In this case, both T
and U can be said to have been deduced.
So my question is twofold:
Does it make sense to think of the argument to the constructor of foo as being
a universal reference in the limited cases in which T has been deduced
within a universal reference context by the caller, as in my example?
In my example, are both uses of std::forward sensible?
make_foo is in the same ballpark as "right", but foo isn't. The foo constructor currently only accepts a non-deduced T &&, and forwarding there is probably not what you mean (but see #nosid's comment). All in all, foo should take a type parameter, have a templated constructor, and the maker function should do the decaying:
template <typename T>
struct foo
{
T v_;
template <typename U>
foo(U && u) : v_(std::forward<U>(u)) { }
};
template <typename U>
foo<typename std::decay<U>::type> make_foo(U && u)
{
return foo<typename std::decay<U>::type>(std::forward<U>(u));
}
In C++14 the maker function becomes a bit simpler to write:
template <typename U>
auto make_foo(U && u)
{ return foo<std::decay_t<U>>(std::forward<U>(u)); }
As your code is written now, int a; make_foo(a); would create an object of type foo<int &>. This would internally store an int, but its constructor would only accept an int & argument. By contrast, make_foo(std::move(a)) would create a foo<int>.
So the way you wrote it, the class template argument determines the signature of the constructor. (The std::forward<T>(v) still makes sense in a perverted kind of way (thanks to #nodis for pointing this out), but this is definitely not "forwarding".)
That is very unusual. Typically, the class template should determine the relevant wrapped type, and the constructor should accept anything that can be used to create the wrapped type, i.e. the constructor should be a function template.
There isn't a formal definition of "universal reference", but I would define it as:
A universal reference is a parameter of a function template with type [template-parameter] &&, with the intent that the template parameter can be deduced from the function argument, and the argument will be passed either by lvalue reference or by rvalue reference as appropriate.
So by that definition, no, the T&& v parameter in foo's constructor is not a universal reference.
However, the entire point of the phrase "universal reference" is to provide a model or pattern for us humans to think about while designing, reading, and understanding code. And it is reasonable and helpful to say that "When make_foo calls the constructor of foo<U>, the template parameter T has been deduced from the argument to make_foo in a way that allows the constructor parameter T&& v to be either an lvalue reference or an rvalue reference as appropriate." This is close enough to the same concept that I would be fine moving on to the claim: "When make_foo calls the constructor of foo<U>, the constructor parameter T&& v is essentially a universal reference."
Yes, both uses of std::forward will do what you intend here, allowing member v_ to move from the make_foo argument if possible or copy otherwise. But having make_foo(my_str) return a foo<std::string&>, not a foo<std::string>, that contains a copy of my_str is quite surprising....
I have some questions concerning function templates.
My plan was to build a wrapper which derives from a user-defined class and
not only exports the public functions of that class but also its constructors.
So I decided I would use multiple constructor templates (which I presume work exactly
the same as function templates) with 1 to n parameters to satisfy most constructors needs.
These would than simply call the constructor and do something else afterwards, like
this:
template <class T>
class Wrapper : public T
{
public:
template <class U>
Wrapper(U &u) : T(u) { doSomething(); }
template <class U, class V>
Wrapper(U &u, V &v) : T(u,v) { doSomething(); }
...
};
My intent is to register the instance within the Wrapper-Ctor somewhere else and,
from that point on, it can receive calls to virtual functions defined in T.
I had to use the reference operator in the code above, in order to guarantee that
my Wrapper-Ctor does not have any side-effects on the parameters that were passed
(copy-construction).
To my surprise this always worked, except for temporaries, which is the reason why
I am confused about the types that are inferred by the compiler in this situation.
To simplify the situation I tried to do something similiar via a template function:
template <class T>
void foo(T &t)
{
int x = ""; // intentional error
}
Calling the function like this:
std::string a;
std::string &b = a;
foo(b);
To my surprise the compiler denotes [T = std::string] in its error message.
I would have expected for this to be [T = std::string&], which would have caused
passing a reference-to-reference, which is invalid.
So, why does the compiler deduce a value-type in this situation?
Is it even possible to create a Wrapper-Ctor that does what I want, does not
have any side-effects on the parameters and also accepts temporaries?
Thanks alot!
It looks like the C++ spec explicitly states that this is the intended behavior. Specifically, if you have a template function that takes in a parameter P that depends on a template type argument, if P is a reference, then the underlying type of the reference, rather than the reference type, is used to determine what type should be used for P (see §14.8.2.1/2). Moreover, this same section says that const and volatile qualifiers are ignored during this step, so the constness can be inferred automatically.
It is not possible in C++03 to provide such a thing without manually overloading for every combination of const and non-const parameters.
No expression ever has reference type. Therefor, when argument deduction deduces against the argument expression type, it cannot make a distinction between a and b because the arguments a and b both have the same type.
Refer to [expr]p5 in the spec
If an expression initially has the type "reference to T" (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis.
Somewhat late, but since I don't think this was answered completely...
For template parameter deduction, see the previous answers.
For your problem with temporaries, make the parameters const references (as in Wrapper(const U&)).
The thing is, temporaries are rvalues. The standard states that non-const references can only be bound to lvalues. Therefore, a standards compliant compiler won't let you pass temporaries(rvalues) as arguments to non-const reference parameters. (This doesn't have anything to do with templates in particular, it's a general rule).
This is to the best of my knowledge, so take it with a bit of scepticism.