decltype(auto)在某些情况下可以与SFINAE一起使用?

时间:2018-07-01 00:24:00

标签: c++ c++17 sfinae

我假设decltype(auto)是不兼容的构造,用于尝试使SFINAE脱离返回类型。因此,当您原本会遇到替代错误时,会遇到硬错误

但是为什么下面的程序起作用? https://wandbox.org/permlink/xyvxYsakTD1tM3yl

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

template <typename T>
class Identity {
public:
  using type = T;
};

template <typename T>
decltype(auto) construct(T&&) {
  return T{};
}

template <typename T, typename = std::void_t<>>
class Foo {
public:
  static void foo() {
    cout << "Nonspecialized foo called" << endl;
  }
};
template <typename T>
class Foo<T,
          std::void_t<typename decltype(construct(T{}))::type>> {
public:
  static void foo() {
    cout << "Specialized foo called" << endl;
  }
};

int main() {
  Foo<Identity<int>>::foo();
  Foo<int>::foo();
}

Foo实例化int时,我们是否会遇到硬错误?鉴于int没有名为type的成员别名?

1 个答案:

答案 0 :(得分:5)

  

我假设decltype(auto)是不兼容的构造,用于尝试使SFINAE脱离返回类型。

它通常是不兼容的,因为它强制实例化函数的主体。如果在 body 中发生替换失败,则将是硬编译错误-SFINAE不适用于此处。

但是,在此示例中,体内出现替换失败的唯一方法是T不是默认可构造的。但是您用T{}调用了construct,它已经要求它是默认可构造的-因此失败将首先发生或永远不会发生。

相反,发生的替换失败是在替换为typename decltype(construct(T{}))::type的直接上下文中。当我们处于将模板参数实例化到::type的直接上下文中时,试图从int中获取Foo的情况发生了,所以SFINAE仍然适用。

一个表明decltype(auto)破坏SFINAE的示例是,如果我们改为将其实现为:

template <typename T>
decltype(auto) construct() {
  return T{};
}

template <typename T, typename = std::void_t<>>
class Foo {
public:
  static void foo() {
    cout << "Nonspecialized foo called" << endl;
  }
};
template <typename T>
class Foo<T,
          std::void_t<typename decltype(construct<T>())::type>> {
public:
  static void foo() {
    cout << "Specialized foo called" << endl;
  }
};

然后尝试实例化:

struct X {
    X(int);
};

Foo<X>::foo(); // hard error, failure is in the body of construct()