有没有办法定义相同类型的可变数量的参数?
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
}
这里的技巧是依靠 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);
}
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()
收到两个 int
s,等等,直到 f()
收到 63 int
s.
下面是一个完整的编译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) {}
您可以使用 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();
我不知道如何实现具有可变数量的相同类型参数的函数。
我正在为一个堆栈和内存很少的微控制器编写代码,所以我不能使用递归或 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
}
这里的技巧是依靠 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);
}
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()
收到两个 int
s,等等,直到 f()
收到 63 int
s.
下面是一个完整的编译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) {}
您可以使用 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();