Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Argument type deduction, references and rvalues

Consider the situation where a function template needs to forward an argument while keeping it's lvalue-ness in case it's a non-const lvalue, but is itself agnostic to what the argument actually is, as in:

template <typename T>
void target(T&) {
    cout << "non-const lvalue";
}

template <typename T>
void target(const T&) {
    cout << "const lvalue or rvalue";
}


template <typename T>
void forward(T& x) {
    target(x);
}

When x is an rvalue, instead of T being deduced to a constant type, it gives an error:

int x = 0;
const int y = 0;

forward(x); // T = int
forward(y); // T = const int
forward(0); // Hopefully, T = const int, but actually an error
forward<const int>(0); // Works, T = const int

It seems that for forward to handle rvalues (without calling for explicit template arguments) there needs to be an forward(const T&) overload, even though it's body would be an exact duplicate.

Is there any way to avoid this duplication?

like image 643
uj2 Avatar asked Jan 02 '11 13:01

uj2


People also ask

What is template argument deduction?

Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.

What is type deduction?

Type inference or deduction refers to the automatic detection of the data type of an expression in a programming language. It is a feature present in some strongly statically typed languages. In C++, the auto keyword(added in C++ 11) is used for automatic type deduction.

What are rvalue references used for?

Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.

What is && type C++?

and, && (C++) The and operator is an alternative representation of the && operator (binary or logical AND). If both operands have a value of true, the result has the value true. Otherwise, the result has the value false. Both operands are implicitly converted to bool and the result type is bool.


3 Answers

This is a known problem and the purpose of rvalue references in C++0x. The problem has no generic solution in C++03.

There is some archaic historical reason why this occurs that is very pointless. I remember asking once and the answer depressed me greatly.

like image 174
Puppy Avatar answered Sep 18 '22 02:09

Puppy


In general, this nasty problem with templates ought to require duplication because the semantics where a variable is const or not, or a reference or not, are quite distinct.

The C++11 solution to this is "decltype" but it is a bad idea because all it does is compound and already broken type system.

No matter what the Standard or the Committee says, "const int" is not and will never be a type. Nor will "int&" ever be a type. Therefore a type parameter in a template should never be allowed to bind to such non-types, and thankfully for deduction this is the case. Unfortunately you can still explicitly force this unprincipled substitution.

There are some idiotic rules which try to "fix" this problem, such as "const const int" reducing to "const int", I'm not even sure what happens if you get "int & &": remember even the Standard doesn't count "int&" as a type, there is a "int lvalue" type but that's distinct:

int x;       // type is lvalue int
int &y = x;  // type is lvalue int

The right solution to this problem is actually quite simple: everything is a mutable object. Throw out "const" (it isn't that useful) and throw away references, lvalues and rvalues. It's quite clear that all class types are addressable, rvalue or not (the "this" pointer is the address). There was a vain attempt by the committee to prohibit assigning to and addressing rvalues.. the addressing case works but is easily escaped with a trivial cast. The assignment case doesn't work at all (because assignment is a member function and rvalues are non-const, you can always assign to a class typed rvalue).

Anyhow the template meta-programming crowd have "decltype" and with that you can find the encoding of a declaration including any "const" and "&" bits and then you can decompose that encoding using various library operators. This couldn't be done before because that information is not actually type information ("ref" is actually storage allocation information).

like image 37
Yttrill Avatar answered Sep 21 '22 02:09

Yttrill


When x is an rvalue

But x is never an rvalue, because names are lvalues.

Is there any way to avoid this duplication?

There is a way in C++0x:

#include <utility>

template <typename T>
void forward(T&& x)   // note the two ampersands
{
    target(std::forward<T>(x));
}

Thanks to reference collapsing rules, the expression std::forward<T>(x) is of the same value-category as the argument to your own forward function.

like image 33
fredoverflow Avatar answered Sep 18 '22 02:09

fredoverflow