就SFINAE而言,访问不存在的成员是否不被视为“错误”?

时间:2019-02-05 04:37:20

标签: c++ templates sfinae

我为漂亮的打印对实现了重载:

template<typename P>
ostream &operator<<(ostream &os, const P &p) {
  using std::operator<<;
  os << '(' << p.first << ", " << p.second << ')';
  return os;
}

但是,即使存在选择 应该很明显的情况,编译器也很难判断是应该应用标准重载还是上面定义的重载:

int main() {
  cout << "SFINAE sure is hard to grasp!\n";
}

error: use of overloaded operator '<<' is
      ambiguous (with operand types 'std::ostream' (aka 'basic_ostream<char>') and
      'const char [30]')

我不太了解问题所在。我要打印的char数组显然没有firstsecond成员,因此使用重载实例化它会导致错误。

SFINAE不应尝试执行替换,发现缺少成员并丢弃结果吗?如果没有,为什么?

2 个答案:

答案 0 :(得分:6)

SFINAE在过载解析期间起作用:

  

此规则适用于函数模板的重载解析:如果将显式指定或推导的类型替换为template参数失败,则会从重载集中舍弃特殊化,而不会引起编译错误。

这意味着只有功能模板的签名对SFINAE才会生效,实现不会被检查。

您可以将重载更改为

template <typename T, typename = void>
struct pairable : std::false_type {};

// check whether type T has first and second members
template <typename T>
struct pairable<T, std::void_t<decltype(std::declval<T>().first),
                               decltype(std::declval<T>().second)>>
    : std::true_type {};

// the template parameter is the part of the signature of function template
template<typename P, std::enable_if_t<pairable<P>::value>* = nullptr>
ostream &operator<<(ostream &os, const P &p) {
  using std::operator<<;
  os << '(' << p.first << ", " << p.second << ')';
  return os;
}

LIVE

答案 1 :(得分:5)

SFINAE仅适用于函数签名:即参数类型,返回类型和限定符,例如:const,非const。它不适用于功能的实现。

在您的情况下,函数签名与调用匹配。因此,该函数被实例化,并有望在没有错误的情况下进行编译。