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

Bre*_*ier 40 c++ parameters templates variadic-functions c++11

我正在创建一个函数(可能是成员函数,而不是它很重要......也许它确实?)需要接受未知数量的参数,但我希望它们都是相同的类型.我知道我可以传入数组或向量,但我希望能够直接接受args列表而无需额外的结构甚至是额外的括号.看起来variadic函数本身并不是类型安全的,我不知道如何使用这个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
}
Run Code Online (Sandbox Code Playgroud)

要么

template<Maiden... Maidens> dragon_list_t make_dragon_list(Maidens...) {
    //here be dragons
}
Run Code Online (Sandbox Code Playgroud)

用法

dragon_list_t dragons_to_slay
    = make_dragon_list(Maiden.Eunice, Maiden.Helga, Maiden.Aida)
;
Run Code Online (Sandbox Code Playgroud)

尝试了类似于上面的一些事情,没有骰子.建议?我可能做出明显的疏忽?我知道这样做可能不是一件大事:

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})
;
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*itb 31

您可以通过可变参数模板接受参数,并让typechecking在转换后检查有效性.

您可以检查功能接口级别的可转换性,以利用重载决策来拒绝完全错误的参数,例如,使用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...);
Run Code Online (Sandbox Code Playgroud)

对于你的用例,如果你知道从a 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
}
Run Code Online (Sandbox Code Playgroud)

如果将此与上述is_convertible方法结合使用,则会有一个拒绝早期模板,该模板也会对参数执行重载解析,如果不适用则拒绝它们.

  • +1给予OP完全符合他的要求,尽管它好坏 (2认同)
  • @Todd拒绝 - 早期模板只检查参数的可转换性为`ToType`(`Dragon`),而不是彼此的参数.因此检查D1-> B并且D2-> B也是如此,但是不检查D1-> D2. (2认同)

Mot*_*tti 16

如果不使用template不在包中的参数,则可变参数函数将解析为具有相同类型的所有参数.

以下max是仅接受ints(或可转换为int)的扩展函数的示例.

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...));
}
Run Code Online (Sandbox Code Playgroud)

说明:解压缩参数pack(args...)时,编译器会查找最佳重载.如果包只有一个参数,那么唯一的候选者是maximum(int)唯一的参数必须是和类型int(或可转换为int).如果包中有多个元素,则唯一的候选者是maximum(int, typename...)第一个参数必须是类型int(或可转换为int).通过归纳证明包装中的所有类型必须是可转换的类型,这很简单int.

  • @BrettRossier 很久以前就问过,但对于任何想知道一个例子可能会有所帮助的人:“maximum(A, B, C)”被评估为“std::max(A, Maximum(B, C))”,依次评估作为`std :: max(A,std :: max(B,maximum(C)))`:在扩展过程中,第一个A和后来的B(通常是除最后一个值之外的所有值)最终成为distinct的第一个参数调用“maximum”的可变参数形式,因此它们被传递给“int n”参数,而 C 最终传递给“maximum(int n)”。因此,所有参数都必须是“int”。 (2认同)

Jer*_*fin 11

由于您已经包含了C++ 0x标记,因此显而易见的答案是查找初始化列表.初始化列表允许您指定ctor的多个参数,这些参数将自动转换为单个数据结构以供ctor处理.

它们的主要(独占?)用途恰好是你提到的那种情况,传递了许多相同类型的参数,用于创建某种列表/数组/其他对象集合.它将得到支持(例如)std::vector,所以你可以使用类似的东西:

std::vector<dragon> dragons_to_slay{Eunice, Helga, Aida};
Run Code Online (Sandbox Code Playgroud)

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


Cof*_*ode 10

尽管问题被标记为 C++11,但我认为 C++20 + 概念解决方案值得添加,因为 GCC 现在已经支持,其他人很快就会跟进。

首先定义一个简单的概念

class mytype{};

template<typename T>
concept MyType = std::is_same<T, mytype>::value;
Run Code Online (Sandbox Code Playgroud)

然后只需使用可变模板参数

template<MyType ... Args>
void func(Args &&... args){
    // do something here
}
Run Code Online (Sandbox Code Playgroud)

随着概念的出现变得更加容易!


Jan*_*šek 10

最近的一个提议,同构可变参数函数,通过使类似你的第一个构造合法的东西来解决这个问题。除了当然要使用参数包,您还必须对其进行命名。此外,确切的语法似乎还不是很具体。

因此,根据提案,这实际上是合法的(您可以在论文的“模板介绍人”段落中看到类似的结构):

dragon_list_t make_dragon_list(Maiden... maidens) {
    //here be dragons
}
Run Code Online (Sandbox Code Playgroud)

  • 无法理解为什么这在语言中还不存在!我一直想要这个。 (7认同)

Fle*_*exo 5

我最近需要将参数包限制为仅一种类型,或者至少可以转换为该类型。我最终找到了另一种方法:

#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
}
Run Code Online (Sandbox Code Playgroud)

我喜欢此解决方案的地方是,我也可以将其应用于check_all其他特征。


Liu*_*Sha 5

使用c++17,你可以写

template <class T, class... Ts, class = std::enable_if_t<(std::is_same_v<T, Ts> && ...)>
void fun(T x, Ts... xs)
{
}
Run Code Online (Sandbox Code Playgroud)