Inference of type of argument, reference and value

Consider a situation where a function template must pass an argument, while preserving the value of lvalue-ness if it is not an lvalue constant, but in itself does not depend on what is actually an argument, for example:

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 a value of r and not T is inferred 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 explicit template arguments) there should be forward(const T&) overload forward(const T&) , even if this body is an exact duplicate.

Is there any way to avoid this duplication?

+6
c ++ reference type-inference templates rvalue
source share
4 answers

This is a known issue and rvalue link assignment in C ++ 0x. The problem has no general solution in C ++ 03.

There is an archaic historical reason why this happens, which is very meaningless. I remember asking once, and the answer overwhelmed me greatly.

+5
source share

When x is rvalue

But x never an rvalue, because names are lvalues.

Is there any way to avoid this duplication?

In C ++ 0x there is a way:

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

Thanks to link collapse rules, the expression std::forward<T>(x) has the same category of values ​​as the argument to your own direct access function.

+2
source share

In general, this unpleasant problem with templates should require duplication, because the semantics, where the variable is const or not, or the link or not, are quite different.

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

Regardless of what the Standard or the Committee says, "const int" is and never will be a type. Nor will "int &" ever be a type. Therefore, the type parameter in the template should never be associated with such non-types, and, fortunately, for deduction this is so. Unfortunately, you can still explicitly force this unprincipled lookup.

There are some idiotic rules that try to "fix" this problem, for example, "const const int", decreasing to "const int", I'm not even sure what will happen if you get "int and &": remember, even the Standard is not considered "int &". as a type, there is a type "int lvalue", but which is different:

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

The correct solution to this problem is actually quite simple: everything is a mutable object. Throw away "const" (this is not so useful) and throw away links, lvalues ​​and rvalues. It is obvious that all types of classes are addressed, rvalue or not (the pointer to "this" is the address). The committee directed a vain attempt to prohibit the appointment and appeal of rvalues. The addressing case works, but it's easy to get away with a trivial cast. The assignment case does not work at all (since assignment is a member function, and r values ​​are not constants, you can always assign the class typed by rvalue).

In any case, the crowd of template metaprograms has "decltype", and with this you can find the encoding of the declaration, including any "const" and "&". bit, and then you can decompose this encoding using various library operators. This cannot be done before, because this information is not typical information ("ref" is actually memory allocation information).

+2
source share

Assuming there are k arguments, as I understand it, the only "solution" in C ++ 03 is to manually write out 2 ^ k forwarding functions using every possible combination of & and const& parameters. To illustrate, suppose target() actually took 2 parameters. Then you will need:

 template <typename T> void forward2(T& x, T& y) { target(x, y); } template <typename T> void forward2(T& x, T const& y) { target(x, y); } template <typename T> void forward2(T const& x, T& y) { target(x, y); } template <typename T> void forward2(T const& x, T const& y) { target(x, y); } 

Obviously this is very cumbersome for large k, so rvalue references in C ++ 0x are mentioned in other answers.

0
source share

All Articles