【问题标题】:Member function template with the number of parameters depending on an integral template parameter成员函数模板,其参数数量取决于一个完整的模板参数
【发布时间】:2012-02-01 17:51:29
【问题描述】:

我有以下类模板:

template<class T, unsigned N>
class MyClass;

其中T 是某种类型,N - 组件数。可以使用MyClass{a1, a2, a3} 初始化类,其中参数的数量等于N

我想添加满足以下要求的MyClass 的成员函数模板(我们将其命名为foo):

  1. 它由另一种类型T2(即template&lt;class T2&gt; void foo(..))模板化
  2. 它接受足够的数据来构造MyClass&lt;T,N&gt;,但不能少也不能多。违反此规则会导致编译时错误。
  3. 它从参数的类型推导出T2。 IE。我希望可以调用foo({a1, a2, a3})foo(a1, a2, a3) 或类似名称,而无需每次都输入&lt;double&gt;MyClass&lt;double,N&gt;

有没有办法实现该功能,从而满足上述要求?

我已经考虑和/或尝试过以下解决方案:

1) 显而易见的:

...
template<class T2>
void foo(MyClass<T2, N> arg);  
...
a.foo({1,2,3}); //compile-time error

原则上不能工作,因为花括号初始化列表是非推导上下文,因此它们不能推导 任何 类型。太可惜了,如果能成功我会很高兴的。

2) initializer_list

原则上不能工作,因为它无法在编译时检查参数的数量。

3) 可变参数模板魔术

类似下面的函数会很简洁:

template<class...T2, class std::enable_if<sizeof...(T2) == N, int>::type = 0>
void foo(T2... args);
..
foo(1,2,3);

但是,我无法让它工作 - 仍然无法推断出 T2。也许有人知道为什么?我使用的是 GCC4.7 20120121 快照。

4) 丑陋的

基本上这与上面的相同,只是扩展为不同 N 的几个重载。我最好重新实现 MyClass 作为不同 Ns 的一组特化,而不是使用这个。

template<class T2, class std::enable_if<N == 1, int>::type = 0>
void fun(T2 a1); //if N == 1
template<class T2, ..>
void fun(T2 a1, T2 a2); //if N == 2
template<class T2, ..>
void fun(T2 a1, T2 a2, T2 a3); //if N == 3
...

【问题讨论】:

    标签: c++ function parameters c++11


    【解决方案1】:

    您的第三个变体的第二个非类型参数应具有前缀 typename 而不是 class

    template<class...T2, typename std::enable_if<sizeof...(T2) == N, int>::type = 0>
    void foo(T2... args);
    ..
    foo(1,2,3);
    

    Check it

    我猜 Gcc 4.7.0 快照有一些模板错误,如果你用 gcc 4.6.2/1 试试它应该可以工作。

    【讨论】:

      【解决方案2】:

      为什么不使用static_assert

      template <typename T, size_t N>
      class MyClass
      {
      public:
          template <typename... Args>
          void foo(Args&&... args)
          {
              static_assert(sizeof...(Args) == N, "Wrong number of arguments.");
              // Rest of the implementation.
          }
      };
      

      【讨论】:

      • 以这种方式使用 static_assert 有一个警告:重载决议仍然可以选择这个函数,即使它不应该。使用 enable_if 完全消除了过载。例如,考虑我们也有非模板化foo(int,int,int) 的情况。那么如果我们有MyClass&lt;int,2&gt; a,调用a.foo(0.5,0.5,0.5) 会选择不正确的重载并失败。
      【解决方案3】:

      可能是这样的:

      #include <utility>
      #include <type_traits>
      
      template <typename T, unsigned int N>
      class MyClass
      {
          template <typename ...Args>
          typename std::enable_if<std::is_constructible<MyClass<T, N>, Args...>::value>::type
          foo(Args &&... args)
          {
              // for example:
              MyClass<T, N> m(std::forward<Args>(args)...);
              // ...
          }
      };
      

      这仅在MyClass 有一个直接接受相关参数的构造函数(如MyClass(A1, A2, A3))时才有效,但如果MyClass 有一个需要初始化列表的构造函数,我认为它不起作用,也不会如果MyClass 是一个需要大括号初始化的聚合,它就可以工作。

      也就是说,您的 MyClass 看起来不像 可能 可能接受初始化列表,因为您说它必须精确地采用 N 参数,这是 IL 无法保证的。

      【讨论】:

      • 这里的一个问题是,在我的情况下,TT2/Args 不一定彼此相关/可转换。也就是说,is_constructible 检查可能会在有效情况下失败。我们真正要检查的是 MyClass 是否可以从 Args 构造,其中 T2 是要推断的某种类型。
      【解决方案4】:

      可能是,根据您的第三个变体,您可以从初始化列表中提取第一个参数并由此推断类型

      template<class U, class...T2, class std::enable_if<sizeof...(T2) == N-1, int>::type = 0>
      void foo(U u, T2... args)
      {
          MyClass<U, N>({u, args...});
      }
      

      【讨论】:

        猜你喜欢
        • 2017-02-22
        • 2018-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多