代码之家  ›  专栏  ›  技术社区  ›  Flávio Lisbôa

SFINAE和模板函数实例化:为什么在启用SFINAE类型的函数参数中使用模板参数时无法推导?

  •  0
  • Flávio Lisbôa  · 技术社区  · 9 年前

    这几天我在用SFINAE做实验,有些事情让我困惑。为什么 my_type_a my_function

    class my_type_a {};
    
    template <typename T>
    class my_common_type {
    public:
        constexpr static const bool valid = false;
    };
    
    template <>
    class my_common_type<my_type_a> {
    public:
        constexpr static const bool valid = true;
        using type = my_type_a;
    };
    
    template <typename T> using my_common_type_t = typename my_common_type<T>::type;
    
    template <typename T, typename V>
    void my_function(my_common_type_t<T> my_cvalue, V my_value) {}
    
    int main(void) {
        my_function(my_type_a(), 1.0);
    }
    

    G++给了我:

    /home/flisboac/test-template-template-arg-subst.cpp: In function ‘int main()’:
    /home/flisboac/test-template-template-arg-subst.cpp:21:30: error: no matching function for call to ‘my_function(my_type_a, double)’
      my_function(my_type_a(), 1.0);
                                  ^
    /home/flisboac/test-template-template-arg-subst.cpp:18:6: note: candidate: template<class T, class V> void my_function(my_common_type_t<T>, V)
     void my_function(my_common_type_t<T> my_type, V my_value) {}
          ^~~~~~~~~~~
    /home/flisboac/test-template-template-arg-subst.cpp:18:6: note:   template argument deduction/substitution failed:
    /home/flisboac/test-template-template-arg-subst.cpp:21:30: note:   couldn't deduce template parameter ‘T’
      my_function(my_type_a(), 1.0);
                                  ^
    

    my_函数 就像我在 main T 将被推导为函数第一个参数的类型,并且该类型将用于函数的实例化。但是看起来 my_common_type_t<T> my_cvalue 将成为 my_type_a 无论如何,我不明白为什么这不管用。。。

    1 回复  |  直到 9 年前
        1
  •  2
  •   Guillaume Racicot    9 年前

    template <>
    struct my_common_type<int> {
        constexpr static const bool valid = true;
        using type = my_type_a;
    };
    
    template <>
    struct my_common_type<double> {
        constexpr static const bool valid = true;
        using type = my_type_a;
    };
    
    // ...
    
    int main(void) {
        my_function(my_type_a{}, 1.0);
    }
    

    编译器是否选择 my_common_type<int> my_common_type<double>

    如果语言允许在您的情况下进行扣减,那么它必须与什么匹配 T 将在 my_common_type<T>::type 为了得到发送给函数参数的确切类型。显然,这不仅是不可能的,但以我上面的例子来说,它可能有多种选择!

    幸运的是,有一种方法可以告诉编译器 my_common_type<T> T 。技巧的基础是:

    template<typename T>
    using test_t = T;
    
    template<typename T>
    void call(test_t<T>) {}
    
    int main() {
        call(1);
    }
    

    是什么 推断为? int 容易的编译器对这种匹配感到满意。此外,自 test_t test_t<soxething> 已知只有 something .

    此外,这也适用于多个级别的别名:

    template<typename T>
    using test_t = T;
    
    template<typename T>
    using test2_t = test_t<T>;
    
    template<typename T>
    void call(test2_t<T>) {}
    
    int main() {
        call(1); // will also work
    }
    

    template<typename T, typename...>
    using first_t = T;
    

    这是与上面相同的简单匹配,但我们也可以发送一些不使用的参数。我们将在这个未使用的包中制作sfinae。

    现在,重写 my_common_type_t

    template <typename T>
    using my_common_type_t = first_t<T, typename my_common_type<T>::type>;
    

    请注意,这也是可行的:

    template <typename T>
    using my_common_type_t = first_t<T, std::enable_if_t<my_common_type<T>::valid>>;
    

    现在扣款将如期发生! Live (GCC) Live (Clang)

    注意,这个技巧只适用于C++14,因为在这种情况下,sfinae(丢弃的参数)只能保证在C++14之后发生。

    struct 针对你的特点,或使用 public: 使成员 公共,否则GCC将输出虚假错误。