代码之家  ›  专栏  ›  技术社区  ›  Leedehai

具有decltype的C++SFINAE:替换失败成为错误?

  •  10
  • Leedehai  · 技术社区  · 8 年前

    该代码适用于:

    // 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::启用_if 具有成员typedef 类型

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

    2 回复  |  直到 8 年前
        1
  •  5
  •   AndyG    8 年前

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

    • is_iter_of 由一个来自 true_type fals_type

    • 使用 enable_if

    需要了解的关键是,在之前,构造函数需要一个 typename is_iter_of<Iter, T>::type 除了你的 启用_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; }
    };
    

    Demo


    额外的 voider 如果出现以下情况,则不首选模板专门化 *(12月<Iter>()) *整数 )因此,回退基础模板( std::false_type

    std::is_constructible``. In other words, it can still derive from std::false_类型 if the expression is well-formed but it's not constructibe, and true\u type`否则。

        2
  •  4
  •   Guillaume Racicot    8 年前

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

    enable_if

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

    这是因为实例化别名是您尝试应用SFINAE的函数的一部分。对于继承,表达式是被实例化的类的一部分,而不是函数。这就是为什么你会犯严重错误。

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

    事实上,SFINAE的出现是因为 enable_if::type 如果布尔参数为false,则不存在,从而导致SFINAE。

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

    如果您发送的每个类都是 Iter * 操作员可用。自 std::is_constructible

    通过别名,使用 std::启用_if 适用SFINAE。然而,基类方法将仅在以下结果上应用SFINAE: std::启用_if