c++ - 与 SFINAE 中的硬错误混淆

标签 c++ templates c++17 sfinae

关于以下代码(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>()))>;

现在问题是

  1. 为什么代码最初无法编译? Foo<T>::impl() 的实例化是在替换的上下文中,所以它应该工作?
  2. 代入foo_name::foo(T)代替 void_t 的第一个参数会让它编译(见 https://wandbox.org/permlink/g3NaPFZxdUPBS7oj ),为什么?添加一个额外的间接层如何使情况有所不同?
  3. 为什么void_t中的订单有所不同,编译器是否会短路类型包中的表达式?

最佳答案

1) 和 2) 有相同的答案; SFINAE does not workreturn type deduction因为函数的主体是 not in immediate context :

10 - Return type deduction for a function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand. [ Note: Therefore, any use of a specialization of the function template will cause an implicit instantiation. Any errors that arise from this instantiation are not in the immediate context of the function type and can result in the program being ill-formed (17.8.2). — end note ]

3) 是 a more interesting question ;短路是故意的,由 [temp.deduct] 保证:

7 - [...] The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered.

这种短路适用于 gcc、clang 和 ICC,但不幸的是 MSVC(截至 CL 19 2017 RTW)出错了,for example :

template<class T> auto f(T t) -> decltype(t.spork) { return t.spork; }
template<class T> auto g(T t) { return t.spork; }
int x(...);
template<class...> using V = void;
template<class T> auto x(T t) -> V<decltype(f(t)), decltype(g(t))> {}
int a = x(0);

关于c++ - 与 SFINAE 中的硬错误混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45584616/

相关文章:

c++ - 如何在 C++ 中检查给定 "KEY"的映射中是否存在 "VALUE"

c++ - 无法在 Windows 上安装最新的 GCC

c++ - if constexpr 似乎只有在两种情况都有效的情况下才有效

c++ - 自定义 std::fstream、std::filebuf 的上溢和下溢函数不会为每个字符调用

c++ - 可以在 C++17 中模拟 C++2 0's ' operator==(const T&) = default' 吗?

c++ - 在编译时触发 void constexpr?

c++ - vector 删除无需设置 iter 返回值即可工作

c++ - 数组元素的总和作为 constexpr

c++ - 即使显式实例化后也会出错

sql - Go 模板用于连接 slice 中的查询