简体   繁体   中英

Using decay with perfect forwarding

Assume we have 2 functions:

template <typename T> void callDecayExample1(T& t)
{
    std::initializer_list<T> testList2{ 1, 2, 3 };
}

template <typename T> void callDecayExample2(T&& t)
{
    std::initializer_list<std::decay_t<T>> testList{1, 2, 3};
}

and we call them like this:

const int& i = 2;
int j = 10;
int& ref_j = j;

callDecayExample1(i);       // deduced as const int&
callDecayExample1(ref_j);// deduced as int&
callDecayExample1(j);   // deduced as int&

callDecayExample2(i);       // deduced as const int&
callDecayExample2(ref_j);// deduced as int&
callDecayExample2(j);   // deduced as int&

depspite the similar deducing in both functions, in the 2nd function I have to use std::decay_t to compile the application. Why it is so?

I use Visual studio 2019 with the /std:c++17 flag

When you have a function like

template <typename T> void callDecayExample1(T& t)
{
    std::initializer_list<T> testList2{ 1, 2, 3 };
}

then T is only going to get deduced as a non reference type. If you give it a int& , then T becomes int so that t becomes a int& . Because of that you do not need to use decay .

In

template <typename T> void callDecayExample2(T&& t)
{
    std::initializer_list<std::decay_t<T>> testList{1, 2, 3};
}

If you pass a int& then T gets deduced as a int& , and then reference collapsing rules turn int& && into int& for the type of t . This means that without decay you would try to make a std::initializer_list<int&> which you can't do.

It is not correct to say that the deduced types are identical:

#include <iostream>

template <typename T>
void printType()
{
  std::cerr << __PRETTY_FUNCTION__ << std::endl;
}

template <typename T>
void callDecayExample1(T& t)
{
  printType<T>();
  std::initializer_list<T> testList2{1, 2, 3};
}

template <typename T>
void callDecayExample2(T&& t)
{
  printType<T>();
  std::initializer_list<std::decay_t<T>> testList{1, 2, 3};
}

int main()
{
  const int i = 0;
  int j = 1;
  int& ref_j = j;

  callDecayExample1(i);
  callDecayExample1(ref_j);
  callDecayExample1(j);

  callDecayExample2(i);
  callDecayExample2(ref_j);
  callDecayExample2(j);

  return 0;
}

prints:

void printType() [with T = const int]
void printType() [with T = int]
void printType() [with T = int]

void printType() [with T = const int&]
void printType() [with T = int&]
void printType() [with T = int&]

In your situation std::decay removes the extra reference that is present in the second example.

Because T may or may not be a reference type:

template <typename T> void callDecayExample2(T&& t)
{
    if constexpr (std::is_same<T, int&>::value)
        std::initializer_list<std::decay_t<T>> testList{ 1, 2, 3 };
    else
        std::initializer_list<T> testList{ 1, 2, 3 };
}

int val = 5;
callDecayExample2(5)   // T = int
callDecayExample2(val) // T = 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