C++ SFINAE与decltype:替换失败成为错误?

Lee*_*hai 10 c++ sfinae

此代码有效:

// Code A
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;

template <typename T>
struct S {
    template <typename Iter, typename = typename enable_if<is_constructible<T, decltype(*(declval<Iter>()))>::value>::type>
    S(Iter) { cout << "S(Iter)" << endl; }

    S(int) { cout << "S(int)" << endl; }
};

int main()
{
    vector<int> v;
    S<int> s1(v.begin()); // stdout: S(Iter)
    S<int> s2(1);         // stdout: S(int)
}
Run Code Online (Sandbox Code Playgroud)

但是下面的代码不起作用.在下面的代码中,我只想继承std::enable_if,因此如果所选版本具有成员typedef ,则该类is_iter_of将具有成员typedef .typestd::enable_iftype

// Code B
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;

template <typename Iter, typename Target>
struct is_iter_of : public enable_if<is_constructible<Target, decltype(*(declval<Iter>()))>::value> {}

template <typename T>
struct S {
    template <typename Iter, typename = typename is_iter_of<Iter, T>::type>
    S(Iter) { cout << "S(Iter)" << endl; }

    S(int) { cout << "S(int)" << endl; }
};

int main()
{
    vector<int> v;
    S<int> s1(v.begin());
    S<int> s2(1);   // this is line 22, error
}
Run Code Online (Sandbox Code Playgroud)

错误信息:

In instantiation of 'struct is_iter_of<int, int>':
12:30:   required by substitution of 'template<class Iter, class>     S<T>::S(Iter) [with Iter = int; <template-parameter-1-2> = <missing>]'
22:16:   required from here
8:72: error: invalid type argument of unary '*' (have 'int')
Run Code Online (Sandbox Code Playgroud)

错误信息令人困惑:当然我希望模板替换失败..因此可以选择正确的构造函数.为什么SFINAE没有工作Code B?如果invalid type argument of unary '*' (have 'int')冒犯了编译器,编译器也应该发出相同的错误Code A.

And*_*dyG 5

问题在于表达式*int*(declval<Iter>()))无效,因此您的模板失败。您需要另一个模板级别,因此我建议使用void_t方法:

  • is_iter_of一个概念,精简版,从导出true_typefals_type

  • enable_if在类的定义中使用来启用迭代器构造函数。

要理解的关键是,您的构造函数之前需要一个类型,typename is_iter_of<Iter, T>::type除了您enable_if中的struct is_iter_of导致整个事物变形。而且由于没有后备模板,您会遇到编译器错误。

template<class...>
using voider = void;

template <typename Iter, typename Target, typename = void>
struct is_iter_of : std::false_type{};

template <typename Iter, typename Target>
struct is_iter_of<Iter, Target, voider<decltype(*(declval<Iter>()))>> : std::is_constructible<Target, decltype(*(declval<Iter>()))> {};

template <typename T>
struct S {
    template <typename Iter, typename std::enable_if<is_iter_of<Iter, T>::value, int>::type = 0>
    S(Iter) { cout << "S(Iter)" << endl; }

    S(int) { cout << "S(int)" << endl; }
};
Run Code Online (Sandbox Code Playgroud)

演示(C ++ 11)


发生了什么

voider如果*(declval<Iter>())是格式不正确的表达式(*int),则附加选项使模板专业化不是首选,因此选择了后备基本模板(std::false_type)。

否则,它将从std::is_constructible``. In other words, it can still derive fromstd :: false_type if the expression is well-formed but it's not constructibe, andtrue_type 派生。


Gui*_*cot 4

问题是您试图从 扩展std::enable_if,但您在启用 if 中放入的表达式可能无效。由于您使用的类继承自该类,因此您实例化的类继承自无效表达式,因此会出现错误。

为表达式命名的一个简单解决方案enable_if是使用别名而不是类:

template <typename Iter, typename Target>
using is_iter_of = enable_if<is_constructible<Target, decltype(*(declval<Iter>()))>::value>;
Run Code Online (Sandbox Code Playgroud)

使用别名时,SFINAE 仍将按预期工作。

这是因为实例化别名是您尝试应用 SFINAE 的函数的一部分。通过继承,表达式是被实例化的类的一部分,而不是函数的一部分。这就是你遇到严重错误的原因。

问题是,SFINAE 有多种方式应用于您的案例。让我们看看 SFINAE 可能发生在什么地方:

enable_if< //             here -------v
    is_constructible<Target, decltype(*(declval<Iter>()))>::value
>::type
//  ^--- here
Run Code Online (Sandbox Code Playgroud)

确实,SFINAE 会发生,因为enable_if::type如果 bool 参数为 false,则不会存在,从而导致 SFINAE。

但如果你仔细观察,可能不存在另一种类型:decltype(*(std::declval<Iter>()))。如果Iterint,则询问星型运算符的类型是没有意义的。所以 SFINAE 也适用于那里。

Iter如果您发送的每个类都具有可用的运算符,那么您的继承解决方案将会起作用*。由于 withint它不存在,因此您将不存在的类型发送到std::is_constructible,从而使形成基类的整个表达式无效。

有了别名,整个使用表达式std::enable_if都以SFINAE为准。而基类方法只会将 SFINAE 应用于 的结果std::enable_if