使用完美转发的衰减

Using decay with perfect forwarding

假设我们有 2 个函数:

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};
}

我们这样称呼它们:

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&

尽管两个函数的推导相似,但在第二个函数中我必须使用 std::decay_t 来编译应用程序。为什么会这样?

我使用 Visual studio 2019 和 /std:c++17 标志

当你有像

这样的函数时
template <typename T> void callDecayExample1(T& t)
{
    std::initializer_list<T> testList2{ 1, 2, 3 };
}

然后 T 只会被推断为非引用类型。如果你给它一个int&,那么T就变成了int,这样t就变成了int&。因此,您不需要使用 decay.

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

如果您传递 int&,则 T 被推断为 int&,然后引用折叠规则将 int& && 转换为 int& 类型t 个。这意味着如果没有 decay 你会尝试做一个你做不到的 std::initializer_list<int&>

说推导类型相同是不正确的:

#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;
}

打印:

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&]

在您的情况下,std::decay 删除了第二个示例中存在的额外引用。

因为T可能是也可能不是引用类型:

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&