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

decltype(auto) works with SFINAE in some cases?

我假设 decltype(auto) 在用于尝试和 SFINAE 关闭 return 类型时是不兼容的结构。所以,当你本来会得到一个替换错误时,你会得到一个硬错误

但是为什么下面的程序可以运行呢? 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();
}

Fooint 实例化时,我们不应该得到一个硬错误吗?鉴于 int 没有名为 type?

的成员别名

I was under the assumption that decltype(auto) is an incompatible construct when used to try and SFINAE off the return type.

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

但是,在这个例子中,如果 T 不是默认可构造的,那么在主体中出现替换失败的唯一方法是。但是你调用 construct(T{}),它已经要求 T 是默认可构造的——所以失败要么先发生,要么永远不会发生。

相反,发生的替换失败是在替换为 typename decltype(construct(T{}))::type 的直接上下文中发生的。当我们处于将模板参数实例化为 Foo 的直接上下文中时,试图从 int 中获取 ::type,因此 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()