有没有一种方法可以定义相同类型的各种参数?

时间:2019-04-16 21:36:36

标签: c++ variadic-templates

我不知道如何实现具有可变数量的相同类型参数的函数。

我正在为堆栈和内存很少的微控制器编写程序,所以我不能使用递归或STL(有例外的部分)。

有可能实现这种功能吗?

struct S{
int r1;
int r2;
};
template<S* s, int... args> fun(int arg1, int arg2);

扩展为如下所示:

for(int arg:args){ 
s->r1+=7*arg;
}

调用示例:

S s;
const int mode=3, speed=1;

fun<&s,1,2,7,4>(mode,speed);

4 个答案:

答案 0 :(得分:1)

使用C ++ 20概念, 必需 可以将可变参数包中的所有参数都设为相同类型。

不幸的是,从C ++ 20开始,the standard library doesn't have a concept for all_same(两种类型只有std::same_as),但是可以很容易地定义它:

template<class... Ts>
concept all_same = 
        sizeof...(Ts) < 2 ||
        std::conjunction_v<
            std::is_same<std::tuple_element_t<0, std::tuple<Ts...>>, Ts>...
        >;

template<typename... Ts> requires all_same<Ts...>
void foo(Ts&&... ts) {}

代码:https://godbolt.org/z/dH9t-N


注意,在许多情况下不需要相同类型,您可以要求所有参数都具有 常用类型 。要要求基于 test if common type exists 的通用类型,您可以具有以下概念:

template <typename AlwaysVoid, typename... Ts>
struct has_common_type_impl : std::false_type {};

template <typename... Ts>
struct has_common_type_impl<std::void_t<std::common_type_t<Ts...>>, Ts...>
    : std::true_type {};

template <typename... Ts>
concept has_common_type = 
    sizeof...(Ts) < 2 ||
    has_common_type_impl<void, Ts...>::value;

template<typename... Ts> requires has_common_type<Ts...>
void foo(Ts&&... ts) {}

代码:https://godbolt.org/z/5M6dLp

答案 1 :(得分:0)

Unfortunately, there is currently no way to specify a function parameter pack where each parameter is of the same type. The next best thing you can get (as far as I'm aware) would be a function that takes a variable number of arguments of any type as long as the type of all of them is int:

#include <type_traits>

template <typename... Args>
auto f(Args... args) -> std::enable_if_t<(std::is_same_v<Args, int> && ...)>
{
    …
}

void test()
{
    f(1, 2, 3);     // OK
    f(1, 2, 3.0f);  // error
}

live example here

The trick here is to rely on SFINAE to effectively remove all versions of the function where not all the args end up being of type int

For your concrete example, you could do, e.g.:

#include <type_traits>

struct S
{
    int r1;
    int r2;
};

template <S& s, typename... Args>
auto f(Args... args) -> std::enable_if_t<(std::is_same_v<Args, int> && ...)>
{
    ((s.r1 += 7 * args), ...);
}

S s;
const int mode=3, speed=1;

void test()
{
    f<s>(mode, speed);
}

live demo here

答案 2 :(得分:0)

I can't figure out how to implement a function with a variable number of arguments of the same type.

Template argument of the same type or ordinary function arguments of the same type?

The first case is simple (if the type is one admitted for template value types), exactly as you have written

template<S* s, int... args>
fun (int arg1, int arg2);

and you can use they using template folding, if you can use C++17,

template <S* s, int... args>
auto fun (int arg1, int arg2)
 { ((s->r1 += 7 * args), ...); }

or in a little more complicated way before (C++11/C++14)

template <S* s, int... args>
auto fun (int arg1, int arg2)
 { 
   using unused = int[];

   (void)unused { 0, s->r1 += 7 * args ... };
 }

Unfortunately you can call this type of function with compile time known integers so, by example, not with variables

int a = 7;
fun<&s,1,2,a,4>(mode,speed); // compilation error

In this case you need a variadic list of ordinary function arguments of the same type; unfortunately this is a little more complicated.

You can create a typical variadic list of template parameter

template <typename ... Args>
auto fun (Args ... args)

imposing, through SFINAE, that all Args... are deduced or explicated as int (see Michael Kenzel's answer).

Unfortunately this require that every argument is exactly if type int so calling func with (by example) a long int gives a compilation error

fun(1, 2, 3l); // compilation error (3l is a long int, not an int)

Obviously you can relax the SFINAE condition imposing (by example) that all Args... types are convertible (std::is_convertible) to int but isn't exactly has developing a function receiving a variadic number of arguments of the same type.

If you can accept a superior limit to the number of arguments (64, in the following example) and that the function is method (maybe static) of a class, you can create a foo class containing a method f() that receive zero int, one f() that receive one int, one f() that receive two ints, etc, until an f() that receive 63 ints.

The following is a full compiling C++17 example

#include <utility>
#include <type_traits>

struct S
{
    int r1;
    int r2;
};

S s;
const int mode=3, speed=1;

template <typename T, std::size_t>
using getType = T;

template <std::size_t N, typename = std::make_index_sequence<N>>
struct bar;

template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
 {
   static constexpr auto f (getType<int, Is> ... args)
    { ((s.r1 += 7 * args), ...); }
 };

template <S &, std::size_t N = 64u, typename = std::make_index_sequence<N>>
struct foo;

template <S & s, std::size_t N, std::size_t ... Is>
struct foo<s, N, std::index_sequence<Is...>> : public bar<Is>...
 { using bar<Is>::f...; };

int main ()
 {
   foo<s>::f(mode, speed);
 }

In C++14 is a little more complicated because there isn't variadic using so you have to write the foo class in a recursive way.

In C++11 you have also to develop a substitute for std::make_index_sequence/std::index_sequence.

答案 3 :(得分:0)

您可以使用折叠表达式 (c++17) 和概念 (c++20) 功能轻松做到这一点。

这个概念看起来像这样:

template<typename T, typename... Types>
concept is_all_same = (... && std::is_same<T, Types>::value);

如果你希望它们只是相同的类型,你可以这样使用:

template<typename... Types> requires is_all_same<Types...>
void fun();

如果你想让一个函数接受一个特定的类型,你可以这样使用它:

template<is_all_same<int>... Types>
void fun();