I'm learning about templates and tried to implement this method:
template <typename Func, typename Left, typename Right>
void flipArgs(Func* function, Left&& leftArg, Right&& rightArg) {
function(std::forward<Right>(rightArg), std::forward<Left>(leftArg));
}
It takes a function and two parameters and calls the given function with the two parameters flipped.
It works fine with function such as:
void test1(std::string, int) {
}
When I tried this function:
template <typename T>
void test2(T&& a, int) {
}
With:
string s("test");
flip(test2<string>, 42, s);
The compiler (g++ 4.7.1) tells me:
error: cannot bind 'std::basic_string' lvalue to 'std::basic_string&&'
I thought that a function parameter such as T&& was a special case that can bind to rvalue and lvalue references? What am I doing wrong?
I thought that a function parameter such as T&& was a special case that can bind to [rvalues and lvalues]?
It is. It basically means the template can have different instantiations for lvalues and for rvalues.
However... When you explicitly make T be string in test2<string>, you are picking one particular instantiation: void test2(string&&, int). string&& is no longer that special case. string&& can only bind to string rvalues. There isn't one instantiation that binds to both rvalues and lvalues.
In general, I'd recommend against explicitly passing function template parameters (unless those are intended, like std::forward or std::make_unique).
In this case, you could instead force one of the instantiations that binds to lvalues. Something like flip(test2<string&>, 42, s);, which will instantiate void test2(string&, int).
If you really want to pass an argument to flip that can accept both lvalues and rvalues, you need a polymorphic function object:
struct test2 {
template <typename T>
void operator()(T&& a, int) const {
}
};
flip(test2{}, 42, s);
The key here is that the decision of which specialisation to use is not made when passing the argument, but only later on when that argument is used.
For completeness, in C++14 you can actually create anonymous polymorphic function objects with the new lambda syntax:
auto test2 = [](auto&& a, int) {};
flip(test2, 42, s);
Related
I know that for the following function
template <typename T>
void do_something(T&& arg);
the function parameter is a forwarding reference. But in the following case is it still a forwarding reference or an rvalue reference?
template <typename T>
class MyClass
{
void do_something(T&& arg);
};
I think it is still a forwarding reference, but I'm not sure. Furthermore, I'd like to know what can be done to enforce an rvalue reference or a forwarding reference, if the result is not what I intended.
It's an rvalue reference. Forwarding references can only appear in a deduced context. This is just a member function that accepts an rvalue reference to the class template parameter.
You can't force a forwarding reference to be an rvalue reference if you want to maintain template argument deduction for functions. If you don't mind specifying the template argument all over the place, then this will always and only ever give an rvalue reference:
template<typename T> struct identity { using type = T; };
template<typename T> void func(typename identity<T>::type&&);
In retrospect, there actually is a way to maintain deduction but force only rvalue refs to be accepted (besides the self documenting one in Simple's answer). You can provide a deleted lvalue overload:
template<typename T>
void func(T&) = delete;
template<typename T>
void func(T&& s)
{
// ...
}
The lvalue overload is more specialized when passed an lvalue. And on account of being deleted, will give a somewhat clear error message.
Furthermore I like to know, what can be done to enforce an rvalue reference
If you always want an rvalue reference in a deduced context (and not a forwarding reference), then you can use this:
template<
typename T,
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>
>
using rval_ref = T&&;
template<typename T>
void foo(rval_ref<T> s)
{
// ...
}
foo can only be called with an rvalue, and T will not be a reference (i.e. if you call foo with std::string&&, then T will be std::string).
I have a template function:
template<typename T>
void doSomething(T& value) {
// doSomething here
}
All is ok, but passing r-value references:
doSomething(getTempVal());
Producing no matching function for call error, because particular instantiated function template expects an l-value for 1st argument. Is there any workarounds to allow template function taking both lvalue and rvalue references without adding new template?
Yes, just use && instead of &.
It may seem odd, because you might think that this would disallow calling it with lvalues, but actually, if T is itself an lvalue reference type, then T && is just T: it doesn't become an rvalue reference type.
In other words,
template <typename T>
void f(T &&);
int main() {
int k = 1;
f(k); // okay: calls f<int&>
f(2); // okay: calls f<int>
}
Note that T can be deduced as a reference type, and the function body will need to be made to handle that.
The following code works and the overloads are found as expected:
struct HasBuzz
{
void buzz() const {}
};
struct NoBuzz {};
template <typename T>
void foo(T const& t)
{
t.buzz();
}
void foo(NoBuzz const&){}
int main()
{
foo(HasBuzz{});
foo(NoBuzz{});
}
However, if I replace the first overload with a "universal reference" version then it no longer works. The correct overload is not found for NoBuzz.
struct HasBuzz
{
void buzz() const {}
};
struct NoBuzz {};
template <typename T>
void foo(T&& t)
{
t.buzz();
}
void foo(NoBuzz const&){}
int main()
{
foo(HasBuzz{});
foo(NoBuzz{}); // error: NoBuzz has no member function buzz
}
What can I do to make it work?
Simple solution
Add an overload that is callable with an rvalue of type NoBuzz.
void foo(NoBuzz const&){ };
void foo(NoBuzz&&) { }; // overload for rvalues
Note: Depending on your actual use case this might not be enough, because if you pass a non-const lvalue type NoBuzz to foo you'd still instantiate the template, since the two NoBuzz overloads doesn't match. At the end of this post is a more complex, but certainly cleaner, solution.
Explanation
template<class T>
void foo (T&&); // (A)
void foo (NoBuzz const&); // (B)
The problem with your snippet is that your template (A) can be instantiated in such a way that it's a better match than your overload (B).
When the compiler sees that you are trying to call a function named foo with an argument which is an rvalue of type NoBuzz, it will look for all functions named foo taking one argument where a NoBuzz would fit.
Let's say it starts of with your template (A), here it sees that T&& is deducable to any reference type (both lvalue, and rvalue), since we are passing an rvalue T = NoBuzz.
With T = NoBuzz the instantiated template would be semantically equivalent to:
void foo (NoBuzz&&); // (C), instantiated overload of template (A)
It will then continue to your overload (B). This overload accepts a const lvalue reference, which can bind to both lvalues and rvalues; but our previous template instantiation (C) can only bind to rvalues.
Since (C) is a better match than (B), binding rvalues to T&& is prefered over U const&, that overload is selected and you get the behavior you describe in your post.
Advanced solution
We can use a technique called SFINAE to conditionally make it impossible to call the template if the type passed doesn't implement .buzz ().
template <typename T>
auto foo(T&& t) -> decltype (t.buzz ())
{
return t.buzz();
}
The above solution uses a lot of new features of C++11, detailed information is available here:
wikipedia.org - C++11 - auto
wikipedia.org - decltype
Conditional overloading with trailing-return-type possible?
refp's answer explains the behavior you're seeing, and offers a possible solution. Another option is to ensure the foo function template does not make it into the candidate set for overload resolution unless T has a member function named buzz().
template <typename T>
auto foo(T&& t)
-> decltype((void)(t.buzz()), void())
{
t.buzz();
}
After making this change the foo(NoBuzz const&) overload will be selected when you pass it an instance of NoBuzz. Live demo
A detailed explanation of what's going on in the decltype expression in the trailing return type can be found here. The only thing I've done differently here is instead of using three subexpressions, with the middle one being void() to prevent a user defined operator, from being selected, I've cast the result of the first expression to void; the intent and result are identical in both cases.
I have come across a code, where std::forward is used. I have googled about it for a longtime and not able to understand its real purpose and use.
I have seen similar threads in stackoverflow, but still not clear. Can somebody explain it with a simple example?
PS: I have gone through this page, but still not able to appreciate its use. Please do not flag this question duplicate and rather try to help me out.
As the page you linked poses it:
This is a helper function to allow perfect forwarding of arguments
taken as rvalue references to deduced types, preserving any potential
move semantics involved.
When you have a named value, as in
void f1(int& namedValue){
...
}
or in
void f2(int&& namedValue){
...
}
it evaluates, no matter what, to an lvalue.
One more step. Suppose you have a template function
template <typename T>
void f(T&& namedValue){
...
}
such function can either be called with an lvalue or with an rvalue; however, no matter what, namedValue evaluates to an lvalue.
Now suppose you have two overloads of an helper function
void helper(int& i){
...
}
void helper(int&& i){
...
}
calling helper from inside f
template <typename T>
void f(T&& namedValue){
helper(namedValue);
}
will invariably call the first overload for helper, since namedValue is, well, a named value which, naturally, evaluates to an lvalue.
In order to get the second version called when appropriate (i.e. when f has been invoked with a rvalue parameter), you write
template <typename T>
void f(T&& namedValue){
helper( std::forward<T>(namedValue) );
}
All of this is expressed much concisely in the documentation by the following
The need for this function stems from the fact that all named values
(such as function parameters) always evaluate as lvalues (even those
declared as rvalue references), and this poses difficulties in
preserving potential move semantics on template functions that forward
arguments to other functions.
Each expression is in exactly one of the following two value categories: lvalue or rvalue.
Normally if you call a function like:
template<typename T>
void f(T t);
template<typename T>
void g(T t)
{
f(t);
}
The value category of the argument to g is lost between the call to g and f, because named parameters, like local variables, are always lvalues.
By using std::forward and adjusting the parameter to a "universal reference" that uses reference collapsing you can preserve the value category:
template<typename T>
void f(T&& t);
template<typename T>
void g(T&& t)
{
f(forward<T>(t));
}
That's why it's called "forward", because you are "forwarding" the value category on, rather than losing it.
So in the example if you call g with an rvalue, then f will be called with an rvalue - rather than an lvalue.
It is used to preserve the exact type of an argument in templates when passing it to another function. For example:
template<class T>
void wrapper(T&& arg)
{
foo(std::forward<T>(arg)); // Forward a single argument.
}
This works as follows:
If the function wrapper gets a std::string or const std::string&, then foo is called as if arg has type of const std::string&.
If the function wrapper gets a std::string&, then foo is called as if arg has type of std::string&.
If the function wrapper gets a std::string&&, then foo is called as if arg has type of std::string&&.
The problem that std::forward solves is that by the usual rules the type of arg within function is std::string even if we pass std::string&& to wrapper. std::forward allows to inject the actual type of T, be it T, T&, const T& or T&&, to the call site.
It's basic use is you're in function g that has been called like this:
g(T1 p1, T2 p2, /* ... */);
and you want to call function f with exactly the same types:
f(T1 p1, T2 p2, /* ... */);
Okay, this may seem like a silly question, but here it goes:
template <typename T>
void foo(T& x)
{
}
int main()
{
foo(42);
// error in passing argument 1 of 'void foo(T&) [with T = int]'
}
What is preventing C++ to instantiate the foo function template with T = const int instead?
The problem is that template type deduction has to work out an exact match, and in that particular case, because of the reference in the signature, an exact match requires an lvalue. The value 42, is not an lvalue, but rather an rvalue, and resolving T with const int would not yield a perfect match. Since template type deduction is limited to exact matches, that deduction is not allowed.
If instead of using a literal you use a non mutable lvalue, then the compiler will deduce the type appropriatedly, as const int will become a perfect match for the argument:
const int k = 10;
foo( k ); // foo<const int>( const int & ) is a perfect match
Now there is a special rule that enables calling a function that takes a const reference (nonmutable lvalue) with an rvalue, that implies creation of a temporary lvalue which is later bound to the reference, but for that rule to kick in the function has to have that signature before hand, which is why explicitly stating that the type of the template is const int works: foo<const int>(42).
Them's the rules ;-). If you leave the compiler to deduce the type from the argument, it picks the simplest thing it can.
It doesn't seem unreasonable to me. Your template is saying it expects a non-const reference, so it doesn't compile with an rvalue.
You could either tell it what you mean at the call site: foo<int const>(42); or change your template to make it clear it doesn't need a mutable reference: template <typename T> void foo(T const & x) { }.
In C++11 you have more options for expressing what your template will and will not accept.
Be it template or normal functions, rvalue cannot be passed by reference. (so const T& works but not T&).
What is preventing C++ to instantiate the foo function template with T = const int instead?
Suppose, C++ allows and makes T = const int instead.
Now after sometime you change foo as,
template<typename T>
void foo (T& x)
{
x = 0;
}
Now compiler has to generate error. For end user the experience will be strange, as for a valid statement like x = 0; it started giving error. That could be the reason that why compiler prevents at the first stage itself!