Я предполагал, что decltype(auto)
является несовместимой конструкцией, когда используется для попытки и SFINAE от возвращаемого типа.
Как правило, это несовместимо, потому что оно вызывает создание экземпляра тела функции. Если в body произойдет сбой подстановки, это будет серьезной ошибкой компиляции - SFINAE здесь не применяется.
Однако, в этом примере, единственный способ получить ошибку замещения в теле - это если бы T
не было конструируемым по умолчанию. Но вы вызываете construct с помощью T{}
, который уже требует, чтобы он был конструируемым по умолчанию - чтобы сбой произошел первым или никогда.
Вместо этого произошедшая ошибка замещения находится в непосредственном контексте подстановки в 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()