简体   繁体   中英

Is it possible for the template parameter to be a reference type?

I have started to learn C++ and currently I am trying to get started with templates, so please bear with me, if my wording is not 100% accurate.

I am using the following literature:

  • C++ Templates: The Complete Guide (2nd Edition)
  • Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14

The first book takes a look at the following template function

template<typename T1, typename T2>
auto max(T1 a, T2 b) -> decltype(b<a?a:b) {
  return b < a ? a : b;
}

and states that this definition has a drawback, since T1 or T2 might be a reference, so that the return type could be a reference type.

However, the second book states, that, if the ParamType , in our case T1 and T2 is neither a pointer nor a reference, which is true for our case, the reference part of the calling expression is ignored.

Illustrated with the example

template<typename T>
void f(T param);

int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // T's and param's types are both int
f(cx); // T's and param's types are again both int
f(rx); // T's and param's types are still both int

Now I am wondering, how could it even be possible, that the return type of the first code snippet is a reference type?

They are both right :

See the code generated in cppinsights

template<typename T1, typename T2>
auto max(T1 a, T2 b) -> decltype(b<a?a:b) {
  return b < a ? a : b;
}

template<typename T1, typename T2>
auto max2(T1 a, T2 b){
  return b < a ? a : b;
}

max(j,i);
max2(j,i);

Will 'generate' :

template<>
int & max<int, int>(int a, int b)
{
  return b < a ? a : b;
}

template<>
int max2<int, int>(int a, int b)
{
  return b < a ? a : b;
}

The trouble is about C++11 -> decltype(b<a?a:b) if you remove it (in C++14 and more) the function will not return a reference anymore

static_assert( is_same_v<decltype(i),int> );
static_assert( is_same_v<decltype((i)),int&> );
static_assert( is_same_v<decltype(i+j),int> );
static_assert( is_same_v<decltype(true?i:j),int&> );

See https://en.cppreference.com/w/cpp/language/operator_other#Conditional_operator

4) If E2 and E3 are glvalues of the same type and the same value category, then the result has the same type and value category [...]

5) Otherwise, the result is a prvalue [...]

In C++ that mean :

static_assert( is_same_v<decltype(true?i:j),int&> ); // E2 and E3 are glvalues 
static_assert( is_same_v<decltype(true?i:1),int> ); // Otherwise, the result is a prvalue

since T1 or T2 might be a reference, so that the return type could be a reference type.

I assume that this is incorrectly quoted. The return type may be a reference type, but for different reasons.

If you go a little further in your second book . In Item 3 you will find the answer to your question.

Applying decltype to a name yields the declared type for that name. Names are typically lvalue expressions, but that doesn't affect decltype's behavior. For lvalue expressions more complicated than names, however, decltype generally ensures that the type reported is an lvalue reference. That is, if an lvalue expression other than a name has type T, decltype reports that type as T& .

There is an implication of this behavior that is worth being aware of, however. In

int x = 0;

x is the name of a variable, so decltype(x) is int. But wrapping the name x in parentheses—“(x)”—yields an expression more complicated than a name. Being a name, x is an lvalue, and C++ defines the expression (x) to be an lvalue, too. decltype((x)) is therefore int&. Putting parentheses around a name can change the type that decltype reports for it!

Now the only question left is:

Is b<a?a:b an lvalue?

From cppreference

4) If E2 and E3 are glvalues of the same type and the same value category, then the result has the same type and value category, and is a bit-field if at least one of E2 and E3 is a bit-field.

So a and b are lvalues and if they are of the same type b<a?a:b will also be an lvalue. See a good explanation here .

This means that the return type for max(1, 2) call will be an int& and for max(1, 2.0) will be int

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM