此代码有效:

// 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)
}

但是下面的这段代码不起作用。在下面的代码中,我只想继承std::enable_if,因此,如果所选版本的is_iter_of具有成员typedef type,则类std::enable_if将具有成员typedef type
// 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
}

错误信息:
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')

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

最佳答案

问题是,您尝试从std::enable_if扩展,但是如果可能,您放入enable中的表达式可能无效。由于您使用的是一个继承该形式的类,因此您实例化的类将从一个无效的表达式继承,从而导致错误。

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

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

SFINAE仍可以使用别名正常工作。

这是因为实例化实例是您尝试对其应用SFINAE的功能的一部分。对于继承,表达式是要实例化的类的一部分,而不是函数。这就是为什么您遇到硬错误的原因。

事实是,在您的情况下应用SFINAE的方法有多种。让我们看一下SFINAE可能发生的地方:
enable_if< //             here -------v
    is_constructible<Target, decltype(*(declval<Iter>()))>::value
>::type
//  ^--- here

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

但是,如果仔细观察,可能不存在另一种类型:decltype(*(std::declval<Iter>()))。如果Iterint,则询问star运算符的类型是没有意义的。因此,也可以在SFINAE上应用。

如果您作为Iter发送的每个类都具有*运算符,则可以使用继承解决方案。由于int不存在,因此您要向std::is_constructible发送不存在的类型,从而使构成基类的整个表达式无效。

使用别名,使用std::enable_if的整个表达式将适用SFINAE。而基类方法仅将SFINAE应用于std::enable_if的结果。

关于具有decltype : substitution failure becomes an error?的C++ SFINAE,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45891716/

10-13 00:06