为所有传递给可变参数或可变参数模板函数的参数指定一个types,使用数组,向量,结构体等?

我正在创build一个函数(可能是成员函数,不是那么重要,也许它是这样做的),它需要接受未知数量的参数,但是我希望所有的参数都是相同的types。 我知道我可以通过一个数组或向量,但我希望能够直接接受参数的列表,没有额外的结构,甚至额外的括号。 它看起来不像variadic函数本身是types安全的,我不知道如何去做这个w / variadic模板函数。 这里基本上是我的目标(更可能不正确的代码,完全不是为了获得龙列表,哈哈):

//typedef for dragon_list_t up here somewhere. enum Maiden { Eunice , Beatrice , Una_Brow , Helga , Aida }; dragon_list_t make_dragon_list(Maiden...) { //here be dragons } 

要么

 template<Maiden... Maidens> dragon_list_t make_dragon_list(Maidens...) { //here be dragons } 

用法

 dragon_list_t dragons_to_slay = make_dragon_list(Maiden.Eunice, Maiden.Helga, Maiden.Aida) ; 

试了几件与上面类似的东西,没有骰子。 build议? 我可能做出的明显疏忽? 我知道这样做可能不是一个大问题:

 dragon_list_t make_dragon_list(std::array<Maiden> maidens) { //here be dragons. } dragon_list_t dragons_to_slay = make_dragon_list({Maiden.Eunice, Maiden.Helga, Maiden.Aida}) ; 

但如果可能的话,我宁愿能够以第一种方式做到这一点。

你可以通过可变参数模板来接受参数,并且在转换后再进行types检查。

可以在函数接口级别检查可转换性,但是要利用重载方式来拒绝彻底的错误参数,例如使用SFINAE

 template<typename R, typename...> struct fst { typedef R type; }; template<typename ...Args> typename fst<void, typename enable_if< is_convertible<Args, ToType>::value >::type... >::type f(Args...); 

对于你的用例,如果你知道从std::array<>到你的dragon_list_t的步骤,那么你已经根据上面的第一个选项(“convert-later”)已经解决了:

 template<typename ...Items> dragon_list_t make_dragon_list(Items... maidens) { std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }}; // here be dragons } 

如果将其与上面的is_convertible方法结合使用,则可以使用拒绝早期模板,该模板也可以对参数进行重载parsing,并在不适用的情况下拒绝它们。

既然你已经包含了C ++ 0x标签,那么显而易见的答案就是查找初始化列表 。 初始化程序列表允许您为ctor指定一些参数,这些参数将自动转换为单个数据结构以供ctor处理。

他们的主要(排他?)使用正是你提到的那种情况,传递了一些相同types的参数来创build某种列表/数组/其他对象集合。 它会被(例如) std::vector ,所以你可以使用如下的东西:

 std::vector<dragon> dragons_to_slay{Eunice, Helga, Aida}; 

创造三个dragon对象的向量。 大多数(所有?)的其他集合都会包含相同的内容,所以如果你真的坚持一个龙的列表,你也应该能够很容易地得到它。

如果不在包中使用template ,则可变参数函数将parsing为具有相同types的所有参数。

下面是一个只接受int s(或可转换为inttypes)的扩展的max函数的例子。

 int maximum(int n) // last argument must be an `int` { return n; } template<typename... Args> int maximum(int n, Args... args) // first argument must be an int { return std::max(n, maximum(args...)); } 

说明:解压参数包( args... )时,编译器会寻找最好的重载。 如果该包只有一个参数,那么唯一的候选者是maximum(int)所以唯一的参数必须是inttypes(或者可转换为int )。 如果包中有多个元素,那么唯一的候选者是maximum(int, typename...)所以第一个参数必须是inttypes(或者可转换为int )。 通过归纳certificate,包中的所有types必须是可转换为int的types)。

我最近需要约束一个参数包只能是一种types,或者至less可以转换为这种types。 我最终find了另一种方式:

 #include <type_traits> #include <string> template <template<typename> class Trait, typename Head, typename ...Tail> struct check_all { enum { value = Trait<Head>::value && check_all<Trait, Tail...>::value }; }; template <template<typename> class Trait, typename Head> struct check_all<Trait, Head> { enum { value = Trait<Head>::value }; }; template <typename ...Args> struct foo { // Using C++11 template alias as compile time std::bind template <typename T> using Requirement = std::is_convertible<double, T>; static_assert(check_all<Requirement, Args...>::value, "Must convert to double"); }; int main() { foo<int, char, float, double>(); foo<int, std::string>(); // Errors, no conversion } 

我喜欢这个解决scheme的事情是,我也可以将check_all应用于其他特性。

这确实取决于你想要实现什么。

通常, enum指示特定类的运行时子types,或区分的联合(boost :: variant)。 但在这种情况下,您想要直接传递enum 。 而且,你有一组有限的可能的值,每个函数调用形成一个子集。 真的,你所代表的是一个子集,而不是几个参数。

表示有限集合的一个子集的最好方法是一个bitset。 大集应该使用std::bitset ; 小集可以使用unsigned long

 enum Maiden_set { Eunice = 1, , Beatrice = 2 , Una_Brow = 4 , Helga = 8 , Aida = 16 }; dragon_list_t make_dragon_list(Maiden_set) { //here be dragons } make_dragon_list( Eunice + Beatrice + Helga ); 

或者,因为你似乎想在编译时处理变化,

 template< int Maidens > // parameter is not a Maiden_set because enum+enum=int dragon_list_t make_dragon_list() { //here be dragons } make_dragon_list< Eunice + Beatrice + Helga >(); // + promotes each enum to int 

应该可以使用enumtypes的operator+重载自动生成2的幂。 但我不确定我是否在正确的轨道上。

我会尽量保持简单,我能想到的最简单的解决scheme就是使用一个普通的旧vector。 通过使用C ++ 0xfunction,您可以得到类似于(即使不完全)所需的语法:

 void foo( std::vector<int> const & v ) { std::copy( v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ") ); } int main() { foo({ 1, 2, 3, 4, 5, 6 }); // note the extra {} } 

总之,你应该创build一个向量。 它没有那么多的开销,特别是如果你使用像boost :: list_of或C ++ 0x的初始化列表。 语法开销最小,而且更灵活(可以通过只在运行时已知的parameter passing列表)。

如果你真的想,你可以使用可变参数模板参数来做到这一点:

 // Using pass-by-value since I'm assuming it is primitive: template< typename T, typename... Args> void make_dragon_list_internal( dragon_list_t* dragon_list, T t, Args... args ) { // add T to dragon_list. make_dragon_list_internal( dragon_list, args... ); } void make_dragon_list_internal( dragon_list_t* dragon_list ) { // Finalize dragon_list. } template<typename... Args> dragon_list_t make_dragon_list( Args... args ) { dragon_list_t dragon_list; make_dragon_list_internal( &dragon_list, args... ); return dragon_list; } 

它是types安全的,可扩展的(如果你愿意的话,你可以把它变成龙以外的东西)。

虽然这个问题被标记为C ++ 11,但我认为C ++ 17 +概念解决scheme值得添加看起来,就好像现在在GCC中有支持一样,而其他的将很快出现。

首先定义一个简单的概念

 class mytype{}; template<typename T> concept bool MyType = std::is_same<T, mytype>::value; 

那么只需使用可变参数模板

 template<MyType ... Args> void func(Args &&... args){ // do something here } 

概念的出现更容易!

我认为下面的代码对你的情况有帮助:

 template <class...> struct IsAllSame {}; template <class T, class B1> struct IsAllSame<T, B1> { static constexpr const bool kValue = std::is_same<T, B1>::value; }; template <class T, class B1, class... Bn> struct IsAllSame<T, B1, Bn...> { static constexpr const bool kValue = IsAllSame<T, B1>::kValue ? IsAllSame<T, Bn...>::kValue : false; }; IsAllSame<int>::kValue == true IsAllSame<bool, int>::kValue == false IsAllSame<bool, int, int>::kValue == false