特定类型的变异模板

问题描述 投票:18回答:4

我想要一个可变参数模板,该模板仅接受无符号整数。但是,我无法执行以下操作。

struct Array
{
    template <typename... Sizes> // this works
    // template <unsigned... Sizes> -- this does not work (GCC 4.7.2)
    Array(Sizes... sizes)
    {
        // This causes narrowing conversion warning if signed int is supplied.
        unsigned args[] = { sizes... };
        // ...snipped...
    }
};

int main()
{
    Array arr(1, 1);
}

感谢任何帮助。

编辑:如果您想知道,我正在尝试使用可变参数模板来复制以下内容。

struct Array
{
    Array(unsigned size1) { ... }
    Array(unsigned size1, unsigned size2) { ... }
    Array(unsigned size1, unsigned size2, unsigned size3) { ... }
    // ...
    Array(unsigned size1, unsigned size2, ..., unsigned sizeN) { ... }
};
c++ gcc c++11 variadic-templates
4个回答
5
投票

我不确定您为什么希望它能正常工作。铛告诉我构造函数的声明中的错误是unknown type name 'Sizes'。这是可以预期的,因为Sizes不是类型(或更确切地说是类型的模板包),所以它是值的模板包。

尚不清楚您到底要在这里做什么。如果将整数值作为模板参数传递,构造函数参数应该是什么?


更新:使用新代码,您只需要一个static_cast<unsigned>()

struct Array
{
    template <typename... Sizes> // this works
    Array(Sizes... sizes)
    {
        unsigned args[] = { static_cast<unsigned>(sizes)... };
        // ...snipped...
    }
};

6
投票

如果要接受必须全部为整数的dynamic参数,则需要一个普通的typename模板,但请检查所有类型都是(可转换为)无符号整数:

#include <type_traits>

struct Array
{
    template <typename ...Args>
    explicit Array(Args ...args,
        typename std::enable_if<all_int<Args...>::value>::type * = nullptr);

    // ...
};

现在您只需要特征:

template <typename...> struct all_int;

template <> struct all_int<> : std::true_type { };

template <typename T, typename ...Rest> struct all_int<T, Rest...>
: std::integral_constant<bool,
       std::is_convertible<T, unsigned int>::value && all_int<Rest>::value>
{ }

如果您希望使类型严格,也可以使用is_same代替is_convertible

另一种选择是完全放弃可变参数模板,并通过接受单个std::initializer_list<unsigned int>使您的类列表可初始化,这提供了更好的数值安全性(例如,禁止缩小转换)。


2
投票

查看initializer list

您可以指定类似的内容

struct Array
{
    Array(std::initializer_list<unsigned> sizes)
    {
        for (auto i = sizes.begin(); i != sizes.end(); ++i)
            ...
    }
}

尽管用法会更改为

Array arr = {1, 1};

0
投票

Backstroy

[当试图或多或少地实现OP要做的事情时,我偶然发现了这个相当古老的问题。我已经实现了类似于@Kerrek SB解决方案的方法,并且正在寻找一种“一般化”此行为的方法(通过提供谓词作为模板结构来进行评估,而不必为不同的谓词重新实现“递归” )。但是,在这样做的同时,我意识到新的CPP20 Concepts功能以一种非常优雅的方式解决了该问题,因此希望共享该解决方案。我没有将概念视为解决方案,因为我在某处读到无法以递归方式声明它们(据我发现,这不是实际负担,因为我可以引用递归类型特征解决方案)。

解决方案

在此解决方案中,我定义了一个自定义概念,因为我认为STL提供的概念不涵盖大多数实际用例,在这种情况下可以使用这些用例。只需将使用的概念替换为“类型名称”,该概念便会应用于所有提供的模板参数。

#include <concepts>
#include <type_traits>

template <typename T> concept unsignedType = std::is_unsigned_v<T>; 

struct Array {
  template <unsignedType... Sizes> Array(Sizes... sizes) {
    unsigned args[] = {sizes...};
  }
};

int main() {
  unsigned k = 12;
  Array arr(k, 1u); 
  //Array arr(33);  // fails as 33 is not unsigned
}

编译

我正在使用gcc-10,在以前的版本中,概念功能可能不可用,因为此功能是实验性的:g ++-10.0 -std = c ++ 2a file.cc

© www.soinside.com 2019 - 2024. All rights reserved.