C++11 可变数量的参数,相同的特定类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18017543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:39:47  来源:igfitidea点击:

C++11 variable number of arguments, same specific type

c++c++11variadic

提问by Skeen

Question is simple, how would I implement a function taking a variable number of arguments (alike the variadic template), however where all arguments have the same type, say int.

问题很简单,我将如何实现一个带有可变数量参数的函数(类似于可变参数模板),但是所有参数都具有相同的类型,比如 int。

I was thinking about something alike this;

我在想类似的事情;

void func(int... Arguments)

Alternatively wont a recursive static assert on the types work?

或者,对类型的递归静态断言不起作用吗?

采纳答案by hmjd

A possible solution is to make the parameter type a container that can be initialized by a brace initializer list, such as std::initializer_list<int>or std::vector<int>. For example:

一种可能的解决方案是使参数类型成为可以由大括号初始化器列表初始化的容器,例如std::initializer_list<int>std::vector<int>例如

#include <iostream>
#include <initializer_list>

void func(std::initializer_list<int> a_args)
{
    for (auto i: a_args) std::cout << i << '\n';
}

int main()
{
    func({4, 7});
    func({4, 7, 12, 14});
}

回答by Jonathan Wakely

Here's a version that removes the function from the overload set, instead of giving a static_assert. This is allows you to provide other overloads of the function that could be used when the types aren't all the same, rather than a fatal static_assert that can't be avoided.

这是一个从重载集中删除函数的版本,而不是提供 static_assert。这允许您提供在类型不完全相同时可以使用的函数的其他重载,而不是无法避免的致命 static_assert。

#include <type_traits>

template<typename... T>
  struct all_same : std::false_type { };

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

template<typename T>
  struct all_same<T> : std::true_type { };

template<typename T, typename... Ts>
  struct all_same<T, T, Ts...> : all_same<T, Ts...> { };

template<typename... T>
typename std::enable_if<all_same<T...>::value, void>::type
func(T...)
{ }

If you want to support perfect forwarding you probably want to decay the types before checking them, so that the function will accept a mix of lvalue and rvalue arguments as long as they have the same type:

如果您想支持完美转发,您可能希望在检查类型之前衰减类型,以便函数将接受左值和右值参数的混合,只要它们具有相同的类型:

template<typename... T>
typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
func(T&&...)
{ }

Alternatively, if you have a general purpose trait for testing the logical conjunction you can do it using std::is_sameinstead of writing your own all_same:

或者,如果您有一个用于测试逻辑连词的通用特征,您可以使用std::is_same而不是编写自己的all_same

template<typename T, typename... Ts>
typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type
func(T&&, Ts&&...)
{ }

Because this requires at least one argument you'd also need another overload to support the zero-argument case:

因为这至少需要一个参数,所以您还需要另一个重载来支持零参数情况:

void func() { }

The and_helper can be defined like so:

and_助手可以定义如下所示:

template<typename...>
  struct and_;

template<>
  struct and_<>
  : public std::true_type
  { };

template<typename B1>
  struct and_<B1>
  : public B1
  { };

template<typename B1, typename B2>
  struct and_<B1, B2>
  : public std::conditional<B1::value, B2, B1>::type
  { };

template<typename B1, typename B2, typename B3, typename... Bn>
  struct and_<B1, B2, B3, Bn...>
  : public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type
  { };

回答by user2746401

I think you can do this by specifying a concrete type when chewing your arguments out of the argument pack. Something like:

我认为你可以通过在从参数包中咀嚼你的参数时指定一个具体类型来做到这一点。就像是:

class MyClass{};
class MyOtherClass{};

void func()
{
    // do something
}

template< typename... Arguments >
void func( MyClass arg, Arguments ... args )
{
    // do something with arg
    func( args... );
    // do something more with arg
}


void main()
{
    MyClass a, b, c;
    MyOtherClass d;
    int i;
    float f;

    func( a, b, c );    // compiles fine
    func( i, f, d );    // cannot convert
}

In the generic case void func( MyClass arg, Arguments ... args )would become void func( arg, Arguments ... args )with a template type T.

在泛型情况下void func( MyClass arg, Arguments ... args )会变成void func( arg, Arguments ... args )模板类型 T。

回答by dusketha

@Skeen How about this?

@Skeen 这个怎么样?

template <typename T>
void func_1(std::initializer_list<T>&& a) {
    // do something
} 

template <typename... T>
void func(T&&... a) {
    func_1({std::forward<T>(a)...});
} 

int main() {
    func(1, 2, 3);
    // func(1, 2, 3, 4.0); // OK doesn't compile
}

回答by iammilind

If you don't want to use brace-based initializer_list/vectorand want to keep the arguments separate in form of argument pack, then below solution checks it at compile time using recursive static_asserts:

如果您不想使用基于大括号的initializer_list/vector并希望以参数包的形式将参数分开,那么下面的解决方案在编译时使用递归static_asserts进行检查:

#include<type_traits>

template<typename T1, typename T2, typename... Error>
struct is_same : std::false_type {};

template<typename T, typename... Checking>
struct is_same<T, T, Checking...> : is_same<T, Checking...> {}; 

template<typename T>
struct is_same<T,T> : std::true_type {};

template<typename... LeftMost>
void func (LeftMost&&... args)
{
  static_assert(is_same<typename std::decay<LeftMost>::type...>::value, 
                "All types are not same as 'LeftMost'");
  // ...
}

int main ()
{
  int var = 2;
  func(1,var,3,4,5);  // ok
  func(1,2,3,4.0,5); // error due to `static_assert` failure
}

Actually this solution would check all the arguments with respect to the first argument. Suppose it was doublethen everything would be checked against double.

实际上,此解决方案将检查与第一个参数有关的所有参数。假设double那时一切都会被检查double

回答by CoffeeandCode

Because I don't think I saw this solution, you could write a specific function for every type (in your case, just int) then a forwarding function taking variadic argument types.

因为我认为我没有看到这个解决方案,所以您可以为每种类型(在您的情况下,只是int)编写一个特定的函数,然后是一个采用可变参数类型的转发函数。

Write each specific case:

写出每个具体案例:

then for each specific case:

然后对于每个特定情况:

// only int in your case
void func(int i){
    std::cout << "int i = " << i << std::endl;
}

Then your forwarding function like this:

然后你的转发功能是这样的:

template<typename Arg0, typename Arg1 typename ... Args>
void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
    func(std::forward<Arg0>(arg0));
    func(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}

This is good because it is expandable for when you want to accept maybe another type too.

这很好,因为当您也想接受其他类型时,它是可扩展的。

Used like this:

像这样使用:

int main(){
    func(1, 2, 3, 4); // works fine
    func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
}