Что касается следующего кода (https://wandbox.org/permlink/nhx4pheijpTF1ohf, воспроизведенного ниже для удобства)
#include <type_traits>
#include <utility>
namespace foo_name {
template <typename T>
void foo();
template <>
void foo<int>();
template <typename T>
struct c_size;
template <>
struct c_size<int> : public std::integral_constant<int, 1> {};
} // namespace foo_name
template <typename Type>
class Foo {
public:
template <typename T>
static decltype(auto) impl(T&& t) {
using foo_name::foo;
return foo(std::forward<T>(t));
}
};
class Something {};
template <typename Type, typename T = std::decay_t<Type>>
using EnableIfHasFoo = std::void_t<
decltype(Foo<T>::impl(std::declval<T>())),
decltype(foo_name::c_size<Type>::value)>;
template <typename Type, typename = std::void_t<>>
class Test {};
template <typename Type>
class Test<Type, EnableIfHasFoo<Type>> {};
int main() {
static_cast<void>(Test<Something>{});
}
Вышеприведенный код выходит с ошибкой, потому что экземпляр Foo<T>::impl()
вызывает жесткую ошибку и не может использоваться в контексте SFINAE. Но странно, что когда вы переключаете порядок вещей в void_t
в EnableIfHasFoo
(на следующий https://wandbox.org/permlink/at1KkeCraNwHGmUI), он будет компилировать
template <typename Type, typename T = std::decay_t<Type>>
using EnableIfHasFoo = std::void_t<
decltype(foo_name::c_size<Type>::value),
decltype(Foo<T>::impl(std::declval<T>()))>;
Теперь вопросы
- Почему код изначально не компилируется? Создание экземпляра
Foo<T>::impl()
находится в контексте замены, поэтому оно должно работать? - Подставляя
foo_name::foo(T)
вместо первого аргумента вvoid_t
, он скомпилирует его (см. https://wandbox.org/permlink/g3NaPFZxdUPBS7oj), почему? Как добавление одного дополнительного слоя косвенности делает ситуацию другой? - Почему порядок в
void_t
имеет значение, компилятор замыкает выражения в пакете типов?