Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 697
polygamma Avatar asked Apr 18 '19 12:04

polygamma


People also ask

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

Which parameter is allowed for non-type template?

Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.

What is template type parameter?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can template parameters have default values?

Template parameters may have default arguments.


2 Answers

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
like image 110
Martin Morterol Avatar answered Sep 30 '22 16:09

Martin Morterol


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 formax(1, 2.0) will be int

like image 24
Nellie Danielyan Avatar answered Sep 30 '22 17:09

Nellie Danielyan