【问题标题】:Enforce variadic template of certain type强制执行某种类型的可变参数模板
【发布时间】:2015-08-01 12:32:13
【问题描述】:

我想强制可变参数模板的类型与之前设置的模板类型相同。在下面的示例中,我希望 T 和 U 是同一类型。

code on ideone.com

#include <iostream>
#include <string>

template<class T>
struct Foo {

    Foo(T val) {
        std::cout << "Called single argument ctor" << std::endl;
        // [...]    
    }    

    // How to enforce U to be the same type as T?
    template<class... U>
    Foo(T first, U... vals) {
        std::cout << "Called multiple argument ctor" << std::endl;
        // [...]   
    }

};

int main() {

    // Should work as expected.
    Foo<int> single(1);

    // Should work as expected.
    Foo<int> multiple(1, 2, 3, 4, 5);

    // Should't work (but works right now). The strings are not integers.
    Foo<int> mixedtype(1, "a", "b", "c");

    // Also shouldn't work. (doesn't work right now, so that is good)
    Foo<int> alsomixedtype(1, 1, "b", "c");
}

【问题讨论】:

标签: c++ templates c++11 variadic-templates


【解决方案1】:

我们可以使用 SFINAE 来确保所有U 类型与T 相同。需要注意的重要一点是,U 不仅仅是您所暗示的一种类型,而是一个可能不同类型的列表。

template<class... U, std::enable_if_t<all_same<T, U...>::value>* = nullptr>
Foo(T first, U... vals) {
    std::cout << "Called multiple argument ctor" << std::endl;
    // [...]   
}

std::enable_if_t 来自 C++14。如果这不适合您,请使用std::enable_if

typename std::enable_if<all_same<T, U...>::value>::type* = nullptr>

all_same 可以通过多种不同的方式实现。这是我喜欢使用布尔包的一种方法:

namespace detail
{
    template<bool...> struct bool_pack;
    template<bool... bs>
    //if any are false, they'll be shifted in the second version, so types won't match
    using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;

template <typename T, typename... Ts>
using all_same = all_true<std::is_same<T,Ts>...>;

【讨论】:

【解决方案2】:

std::conjunction(逻辑与)是在 C++17 中引入的,因此不必再手动实现 all_same。那么构造函数就变得简单了:

template<typename... U,
    typename = std::enable_if_t<
        std::conjunction_v<
            std::is_same<T, U>...
        >
    >
>
Foo(T first, U... vals)
{
    std::cout << "Called multiple argument ctor" << std::endl;
    // [...]   
}

live example

【讨论】:

    【解决方案3】:

    C++20 概念让它变得如此简单

        template<std::same_as<T>... U>
        Foo(T first, U... vals) {
            std::cout << "Called multiple argument ctor" << std::endl;
            // [...]   
        }
    

    https://gcc.godbolt.org/z/neEsvo

    【讨论】:

      【解决方案4】:

      如果要求所有参数是同一类型并且参数的数量是可变的,

      可变参数模板对于这些要求来说太重了,只需使用 C++11 std::initializer_list

      如果您可以在通话中将 () 替换为 {},他们就能胜任。

      template<class T> struct Foo {
          Foo(T val) {
              std::cout << "Called single argument ctor" << std::endl;
          }
          // Enforce all parameters to be the same type :
          Foo( std::initializer_list<T> values ) {
              std::cout << "Called multiple argument ctor" << std::endl;
              for (T value : values)
                  cout << value << endl;
          }
      };
      
      int main() {
          // Work as expected.
          Foo<int> single(1);
          // Work as expected.
          Foo<int> multiple{ 1, 2, 3, 4, 5 };
          // Doesn't work - as required :
          //Foo<int> mixedtype{ 1, "a", "b", "c" };
      
      }
      

      【讨论】:

      【解决方案5】:

      如果不实现all_same,您还可以更改构造函数代码如下:

      template<class F, typename = typename enable_if<is_same<F, T>::value>::type, class... U>
          Foo(F first, U... vals): Foo(vals...) {
          std::cout << "Called multiple argument ctor" << std::endl;
          // [...]   
      }
      

      is_same 是 STL &lt;type_traits&gt; 中的函数

      【讨论】:

      • 硬编码int类型限制了这个函数的使用,
      • @Jagannath 实际上它也可以是任何东西,例如void 因为它只是为了确定类型是否有效
      • @Jagannath 关键是我们递归调用内部类型构造函数的构造函数,直到我们只得到一个参数
      • 是的,int 很好,但是你不需要指定任何东西,因为void 被用作默认参数,所以它只会混淆声明。
      • 确实适用于用例;但是,我不太热衷于递归,因为它需要重新处理现有逻辑,即我需要检测最后一次递归调用的时间。
      猜你喜欢
      • 2023-03-22
      • 2013-07-14
      • 2011-05-04
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多