使用左值引用时为 SFINAE,但使用右值引用时成功

llx*_*xee 0 c++ templates sfinae c++11 c++17

我搜索了但确实找不到答案,为什么仅当参数由左值引用传递时才会发生 SFINAE,但当参数由右值引用传递时构建会成功:

template <typename T>
class A {
 public:
  using member_type = T;
};

template <typename AType>
typename AType::member_type f(AType&& m) {
  typename AType::member_type res{};
  return res;
}

void demo() {
  A<int> a;

  // ERROR: candidate template ignored: substitution failure 
  // [with AType = A<int> &]: type 'A<int> &' 
  // cannot be used prior to '::' because it has no members
  f(a); 

  // BUILD SUCCESS
  f(std::move(a)); 
}
Run Code Online (Sandbox Code Playgroud)

Nat*_*ica 5

当你有

template <typename AType>
typename AType::member_type f(AType&& m)
Run Code Online (Sandbox Code Playgroud)

您拥有所谓的转发参考。尽管它看起来像右值引用,但此引用类型可以绑定到左值或右值。它的工作方式是,当您将左值传递给 时fAType会被推导为 is T&,而当您传递右值时,AType会被推导为 just T

因此,当您确实f(a); AType被推导为 时A<int>&,并且您尝试形成A<int>&::member_type无效的返回类型,因为引用没有类型成员。

相反,当您这样做时f(std::move(a));AType会被推导为A<int>并且A<int>确实有一个member_type类型成员。

std::decay_t要解决此问题,您可以使用like删除类型的引用性

template <typename AType>
auto f(AType&& m) {
  typename std::decay_t<AType>::member_type res{};
  return res;
}
Run Code Online (Sandbox Code Playgroud)