有没有办法定义相同类型的可变数量的参数?

Is there a way to define a variadic number of arguments of the same type?

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

我正在为一个堆栈和内存很少的微控制器编写代码,所以我不能使用递归或 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);

遗憾的是,目前无法指定每个参数都属于同一类型的函数参数包。你能得到的下一个最好的东西(据我所知)是一个函数,它接受任何类型的可变数量的参数,只要它们的类型都是 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

这里的技巧是依靠 SFINAE 有效地删除函数的所有版本,其中并非所有 args 最终都是类型 int

对于您的具体示例,您可以这样做,例如:

#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

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

相同类型的模板参数或相同类型的普通函数参数?

第一种情况很简单(如果类型是模板值类型所承认的类型),就像你写的那样

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

如果你可以使用 C++17,你可以使用模板折叠来使用它们,

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

或者在 (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 ... };
 }

不幸的是,您可以使用编译时已知的整数调用此类函数,例如,不能使用变量

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

在这种情况下,您需要一个相同类型的普通函数参数的可变参数列表;不幸的是,这有点复杂。

您可以创建一个典型的可变参数模板参数列表

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

通过 SFINAE,所有 Args... 都被推导或解释为 int(请参阅 Michael Kenzel 的回答)。

不幸的是,这要求每个参数都恰好是 if 类型 int,因此使用(通过示例)a long int 调用 func 会产生编译错误

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

显然,您可以放宽 SFINAE 条件(例如)所有 Args... 类型都可转换 (std::is_convertible) 为 int,但并不完全具有开发函数接收相同类型的可变数量的参数。

如果您可以接受参数数量的上限(64,在以下示例中)并且该函数是 class 的方法(可能是静态的),您可以创建一个 foo class 包含一个方法 f() 接收零 int,一个 f() 接收一个 int,一个 f()收到两个 ints,等等,直到 f() 收到 63 ints.

下面是一个完整的编译C++17的例子

#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);
 }

在 C++14 中有点复杂,因为没有可变参数 using 所以你必须以递归的方式编写 foo class。

在 C++11 中,您还必须开发 std::make_index_sequence/std::index_sequence 的替代品。

使用 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


注意在许多情况下不需要相同的类型,您可能需要所有参数都具有通用类型。需要普通类型,基于可以有如下概念:

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

您可以使用 fold expression (c++17) 和 concepts (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();