【问题标题】:Is it possible to use 'enable_if' and 'is_same' with variadic function templates?是否可以将“enable_if”和“is_same”与可变参数函数模板一起使用?
【发布时间】:2018-02-13 10:09:28
【问题描述】:

这两个非可变函数模板可以编译:

template <typename T, typename U>
typename std::enable_if<std::is_same<U, int>::value, void>::
type testFunction(T a, U b) {
    std::cout << "b is integer\n";
}

template <typename T, typename U>
typename std::enable_if<std::is_same<U, float>::value, void>::
type testFunction(T a, U b) {
    std::cout << "b is float\n";
}

但是,类似的可变参数模板无法编译:

template <typename T, typename... U>
typename std::enable_if<std::is_same<U, int>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are integers\n";
}

template <typename T, typename... U>
typename std::enable_if<std::is_same<U, float>::value, void>::
type testFunction(T a, U... bs) {
    std::cout << "bs are floats\n";
}

也许我正在尝试做一些无法完成的事情。我知道使用初始化列表可以实现类似的功能,但我想避免使用初始化列表参数所需的大括号。

【问题讨论】:

    标签: c++ variadic-templates enable-if


    【解决方案1】:

    是的。您可以在 C++17 中使用fold expression

    template <typename T, typename... U>
    typename std::enable_if<(std::is_same<U, float>::value && ...), void>::
    type testFunction(T a, U... bs) {
        std::cout << "bs are floats\n";
    }
    

    在C++11中,可以重新实现std::conjunction

    template<class...> struct conjunction : std::true_type { };
    template<class B1> struct conjunction<B1> : B1 { };
    template<class B1, class... Bn>
    struct conjunction<B1, Bn...> 
        : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
    

    template <typename T, typename... U>
    typename std::enable_if<
        std::conjunction_v<std::is_same<U, float>...>, 
        void
    >::type testFunction(T a, U... bs) {
        std::cout << "bs are floats\n";
    }
    

    【讨论】:

    • + 用于 C++17 折叠表达式
    • 折叠表达式从 15.7.5 开始在 VS2017 中存在错误,因此需要替代解决方案,减去 std::conjunction 的重新实现。
    • @VittorioRomeo 使用std::conjunction 的示例有一个小错误-::value 已被conjunction 使用。即,它应该是enable_if&lt;conjunction_v&lt;is_same&lt;U, float&gt;...&gt;, void&gt;
    • 如果我不能使用返回类型,这将如何工作?例如构造函数?
    • @katrasnikj:它在哪里并不重要,只要它是签名的一部分。可以是默认模板参数,也可以是默认函数参数。
    【解决方案2】:

    或者您可以简单地使用额外的可变参数pack 来测试所有模板参数是否相同并且等于给定类型(C++11):

    #include <type_traits>
    #include <iostream>
    
    template <class...>
    struct pack { };
    
    template <typename T, typename... U>
    typename std::enable_if<std::is_same<pack<int, U...>, pack<U..., int>>::value, void>::
    type testFunction(T a, U... bs) {
        std::cout << "bs are integers\n";
    }
    
    template <typename T, typename... U>
    typename std::enable_if<std::is_same<pack<float, U...>, pack<U..., float>>::value, void>::
    type testFunction(T a, U... bs) {
        std::cout << "bs are floats\n";
    }
    
    int main() {
        testFunction(1, 2, 3, 4, 5);
        testFunction(1, 2.0f, 3.5f, 4.4f, 5.3f);
    }
    

    [live demo]

    输出:

    bs are integers
    bs are floats
    

    【讨论】:

      【解决方案3】:

      如果您绑定到 C++ 11 并希望保持代码可读性,您可以实现与多种类型匹配的 is_same 的简单等价物:

      template <typename Ref, typename T1, typename... TN>
      struct all_match;
      
      template <typename Ref, typename T>
      struct all_match<Ref,T>
      {
          static constexpr bool value = std::is_same<T,Ref>::value;
      };
      
      template <typename Ref, typename T1, typename... TN>
      struct all_match
      {
          static constexpr bool value = std::is_same<T1,Ref>::value && all_match<Ref, TN...>::value;
      };
      

      然后(注意,引用类型在前):

      template <typename T, typename... U>
      typename std::enable_if<all_match<int, U...>::value, void>::
      type testFunction(T a, U... bs) {
          std::cout << "bs are integers\n";
      }
      

      现场演示:click

      C++ 17 引入了fold expression,它允许您在二元运算符上折叠参数包 (...)。您可以使用它轻松地将std::is_same&lt;&gt; 应用于参数包中的所有类型,然后and 值:

      template <typename T, typename... U>
      typename std::enable_if<(std::is_same<U, float>::value && ...), void>::
      type testFunction(T a, U... bs) {
          std::cout << "bs are floats\n";
      }
      
      template <typename T, typename... U>
      typename std::enable_if<(std::is_same<U, int>::value && ...), void>::
      type testFunction(T a, U... bs) {
          std::cout << "bs are ints\n";
      }
      

      您可以查看此版本的演示here

      【讨论】:

        猜你喜欢
        • 2014-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 2023-02-04
        • 2015-05-02
        相关资源
        最近更新 更多