【问题标题】:Template overload based on value of parameter基于参数值的模板重载
【发布时间】:2016-08-21 19:04:16
【问题描述】:

我想做的是能够根据参数的值对同一个模板类进行不同的声明,如下所示:

// Enable if X == 2
template <int X, int W, typename Y,
          typename A = int, typename B = int> struct Z {}; 

// Enable if X != 2
template <int X,        typename Y,
          typename A = int, typename B = int> struct Z {};

我可以这样开始:

template <int X, int W, typename Y, typename A = int, typename B = int, 
          typename = std::enable_if_t<X == 2>> struct Z {};
template <int X,        typename Y, typename A = int, typename B = int,
          typename = std::enable_if_t<X != 2>> struct Z {};

问题在于,可以理解的是,它已使用不同数量的参数重新声明。

可变参数模板功能可以派上用场,但不幸的是,它只支持类型而不支持字面量,就像本例一样。

template <typename... Args> struct Z {};
template <int X, int W, typename Y,
          typename A = int typename B = int> struct Z<X, W, Y> {};

类型/值不匹配 -> 期望类型,得到 'X/W'

有人对此有解决方案吗?

编辑

对不起我之前没有提到,但很遗憾我不能改变参数的顺序,因为Y之后的其他参数都有默认值。

【问题讨论】:

  • 这听起来像是一个 XY 问题。你到底想达到什么目标?

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


【解决方案1】:

你说过你想按照这里的 cmets 所说的那样做:

// Enable if X == 2
template <int X, int W, typename Y, typename A=int, typename B=int> 
struct Z {}; 

// Enable if X != 2
template <int X,        typename Y, typename A=int, typename B=int> 
struct Z {};

显然,当X !=2 时,您希望不使用第二个模板参数。你可以不用名字来定义它。

部分特化可用于进行选择:

// PRIMARY TEMPLATE: Used if X != 2
template <int X, int, typename Y, typename A=int, typename B=int> 
                //^^ see here
struct Z {};

// PARTIAL SPECIALIZATION: Used if X == 2
template <int W, typename Y, typename A, typename B> 
struct Z<2, W, Y, A, B> {}; 

编辑 1 of 2: (根据您的评论)

我希望能够使用只有两个参数的结构 Z (if X!=2): Z&lt;3, int&gt;

您必须重新排序模板并在主模板中使用默认参数

// PRIMARY TEMPLATE: Used if X != 2
template <int X, typename Y, int=0, typename A=int, typename B=int> 
                             //^^ see here
struct Z {};

// PARTIAL SPECIALIZATION: Used if X == 2
template <typename Y, int W, typename A, typename B> 
struct Z<2, Y, W> {}; 

你可以用作:

Z<2, int> z;
Z<3, int, 5> z3;

EDIT 2 of 2: (对您的问题编辑和 cmets)

套用你的话:

我希望能够在不重新排序我的模板的情况下执行Z&lt;3, int&gt; 参数

如果第二个模板参数是类型 并且其余参数默认。不幸的是,这不是你的情况。所以如果这真的是你的问题,你就在这里死胡同了。

【讨论】:

  • 测试你的代码,你的模板参数数量错误。
  • 很好,不知道你不能命名参数。但是,它仍然没有解决问题,因为我希望能够使用结构 Z 只有两个参数(如果X!=2):Z&lt;3, int&gt; 给出错误。我将不得不放弃一些东西。
  • @Mehlins,在这种情况下,您将不得不重新排序模板并使用默认参数。查看我的更新答案
  • @Mehlins,我看到你的问题更新了。我已经相应地更新了我的答案。
【解决方案2】:

如果您可以使用 C++14 并且您可以接受您的整数包含在 std::integer_sequence 中,您可以通过这种方式定义 Z 的两个版本

#include <iostream>
#include <utility>

template <typename, typename>
struct Z;

template <int W, typename T>
struct Z<std::integer_sequence<int, W>, T>
 { static constexpr int c { 0 }; };

template <int X, typename T>
struct Z<std::integer_sequence<int, 2, X>, T>
 { static constexpr int c { 2 }; };

int main()
 {
   Z<std::integer_sequence<int, 0>, long>     z0;
   Z<std::integer_sequence<int, 2, 7>, long>  z2;
   Z<std::integer_sequence<int, 2>, long>     zX;  // unacceptable ?

   std::cout << "z0: " << z0.c << std::endl;  // output: z0: 0
   std::cout << "z2: " << z2.c << std::endl;  // output: z2: 2
   std::cout << "zX: " << zX.c << std::endl;  // output: zX: 0
 }

我不知道如何避免定义为 zXX == 2 和没有 W 但是,如果您希望 zX 用作 z2,您可以通过以下方式定义 Z

template <typename, typename>
struct Z;

template <typename T, int ... I>
struct Z<std::integer_sequence<int, I ...>, T>
 { static constexpr int c { 0 }; };

template <typename T, int ... I>
struct Z<std::integer_sequence<int, 2, I ...>, T>
 { static constexpr int c { 2 }; };

zX 返回2

第二个解决方案的问题是,即使Z 使用未定义的整数进行编译。我的意思是编译(使用第二种解决方案)

 Z<std::integer_sequence<int, 0, 5, 6, 7>, long>     z0;

--- 编辑---

对不起,我是个白痴。

避免zX的定义很简单。

在这两种情况下,您都可以声明(但不能定义)以下特化。

template <typename T>
struct Z<std::integer_sequence<int, 2>, T>;

【讨论】:

    【解决方案3】:

    如果不重新排序参数或使用其他数据结构来包装参数,您将无法执行此操作。

    无论如何,您可以使用constexpr 构造函数来提交额外的参数,并能够在常量表达式中使用您的类型。
    举个例子:

    template <int X, typename Y, typename A = int, typename B = int>
    struct Z {
        constexpr Z() {}
    }; 
    
    template <typename Y, typename A, typename B>
    struct Z<2, Y, A, B> {
        constexpr Z(int W) { }
    };
    
    int main() {
        constexpr Z<0, void> z1;
        constexpr Z<2, void> z2{42};
    }
    

    如果这是一个好的折衷方案,主要取决于真正的问题。

    【讨论】:

      【解决方案4】:

      如果您可以更改模板参数的顺序,您可以专门化您的类并使用默认值:

      // Chosen if X != 2
      template<int X, typename Y, typename A = int, typename B = int, int = 0>
      struct Z {}; // W is discarded
      
      // Chosen if X == 2
      template<typename Y, typename A, typename B, int W>
      struct Z<2, Y, A, B, W> {};
      

      Live example

      【讨论】:

      • 就像我在另一个回复中所说的那样,它并没有解决问题,因为我希望能够使用只有两个参数的结构 Z(如果 X!=2):Z&lt;3, int&gt; 给出错误.我将不得不放弃一些东西。
      • @Mehlins 已编辑。通过使用默认值,您可以编写类似Z&lt;3, int&gt; 的内容。您只需要对参数重新排序,使默认的参数出现在末尾即可。
      • 是的,我也注意到了,很抱歉我没有在帖子中提到它,但这只是真实结构的一个示例,它更长更复杂,不幸的是我无法更改事物的顺序。在我的例子中,Y 和之后的更多参数都有默认值。
      • @Mehlins 然后把真正的结构放在你的问题中。我们不能用我们没有的东西工作。也就是说,如果您的模板有很多参数,那么您可能做错了。
      • @Mehlins 为什么不能在已经默认的参数之前重新排序参数?您必须遵循的唯一规则是将每个默认参数放在非默认参数之后。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-25
      • 1970-01-01
      • 2021-01-01
      • 2023-03-12
      • 1970-01-01
      相关资源
      最近更新 更多