问题很简单,我如何实现一个带有可变数量参数的函数(类似于可变参数模板),但是如果所有参数都具有相同的类型,比如int。
我正在考虑类似的事情;
void func(int... Arguments)
或者,对类型的递归静态断言不起作用吗?
答案 0 :(得分:44)
一种可能的解决方案是使参数类型成为可以通过大括号初始值设定项列表初始化的容器,例如std::initializer_list<int>
或std::vector<int>
。 For example:
#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});
}
答案 1 :(得分:17)
这是一个从重载集中删除函数的版本,而不是给出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...)
{ }
如果你想支持完美的转发,你可能想在检查它们之前衰减它们,这样只要它们具有相同的类型,该函数就会接受左值和右值的混合:
template<typename... T>
typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
func(T&&...)
{ }
或者,如果您有用于测试逻辑联合的通用特征,则可以使用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&&...)
{ }
因为这需要至少一个参数,你还需要另一个重载来支持零参数的情况:
void func() { }
可以像这样定义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
{ };
答案 2 :(得分:11)
我认为你可以通过在从参数包中咀嚼你的参数时指定一个具体的类型来做到这一点。类似的东西:
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
}
在一般情况下,void func( MyClass arg, Arguments ... args )
将成为void func( arg, Arguments ... args )
,模板类型为T。
答案 3 :(得分:4)
@Skeen 怎么样?
<table id="mydata">
<thead>
<tr>
<th>Name</th>
<th>Phone no</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Smith</td>
<td>1234567890</td>
</tr>
</tbody>
</table>
答案 4 :(得分:3)
如果你不想使用基于大括号的initializer_list
/ vector
并希望以参数包的形式保持参数分开,那么下面的解决方案在编译时使用recursive {{ 1}} S:
static_assert
实际上这个解决方案会检查与第一个参数有关的所有参数。假设它是#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
}
,那么将根据double
检查所有内容。
答案 5 :(得分:1)
因为我认为我没有看到这个解决方案,你可以为每种类型编写一个特定的函数(在你的情况下,只是int
)然后是一个转换函数,它采用可变参数类型。
写下每个具体案例:
然后针对每个具体案例:
// only int in your case
void func(int i){
std::cout << "int i = " << i << std::endl;
}
然后您的转发功能如下:
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)...);
}
这很好,因为当你想要接受另一种类型时,它是可扩展的。
像这样使用:
int main(){
func(1, 2, 3, 4); // works fine
func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
}