我有一个类,其模板接受一个整数:
template <unsigned int N>
class Example {};
我正在寻找一种方法来定义一个(成员)函数,它接受一些Example
对象作为参数。金额将由N
确定,因此函数将使用如下:
Function(Example<2>(), Example<2>());
Function(Example<3>(), Example<3>(), Example<3>());
到目前为止我尝试了什么:
使用初始化列表,可以将一组对象传递给函数:
template <unsigned int N>
void Function(std::initializer_list<Example<N>> list);
//...
Function({Example<2>(), Example<2>()});
但是,除了真正只传递一个参数(列表)之外的问题是,使用此方法可以使用任意数量的参数:
Function({Example<2>()});
我也尝试使用可变参数函数:
template <unsigned int N>
void Function(Example<N> e...)
{
va_list args;
va_start(args, e);
//...
}
Function(Example<2>(), Example<2>());
这使得可以使用真实参数,但仍然存在使用任意数量参数的问题,并且不可能知道实际传递了多少参数。
假设您希望从Example<N>
类型中推导出参数的数量,并且所有Example<I>
应该共享相同的N
,那么C ++ 17解决方案可能是
template <unsigned int... I>
auto Function( Example<I>... ) ->
std::enable_if_t<( ( I == sizeof...(I) ) && ... )>
{
// or static_assert() if you always want an error
}
使Function
成为一个可变参数模板,并使用std::enable_if_t
来限制它:
IsExample
特性可用于确保所有参数都是Example
的实例sizeof...(pack)
可用于获取参数包的大小template <unsigned int N, typename... Ts>
auto Function(Ts... xs)
-> std::enable_if_t<(IsExample<Ts>::value && ...)
&& (sizeof...(Ts) == N)>
{
}
您应该使用variadic函数模板和static_assert。与涉及enable_if
的方法不同,如果传递的参数不正确,则会产生可读的错误消息。
template<unsigned int ... I>
void Function(Example<I>... items)
{
static_assert
(
true && (... && (static_cast<unsigned int>(sizeof...(I)) == I))
, "This function accepts N arguments of type Example<N>"
);
}
有许多答案涵盖SFINAE友好的约束,但我不喜欢将我的SFINAE放在返回值中:
template <unsigned int... Is,
std::enable_if_t<( ( Is == sizeof...(Is) ) && ... ), bool> = true
>
void Function( Example<Is>... examples )
{
// code
}
要么
template<bool b>
using test_requirement = std::enable_if_t<b, bool>;
template <unsigned int... Is,
test_requirement<( ( Is == sizeof...(Is) ) && ... )> = true
>
void Function( Example<Is>... examples )
{
// code
}
为Massimiliano Janes的优雅解决方案+1。
不幸的是,使用折叠只适用于C ++ 17。
要使用C ++ 11 / C ++ 14测试所有I
等于sizeof...(I)
(也许sizeof...(I)
等于N
,其中N
是类模板参数),它足以测试一个可变参数类型,它接收无符号值,是具有不同值顺序的相同类型。
我的意思是:声明一个简单的结构
template <std::size_t ... Is>
struct IList;
测试可以
std::is_same<IList<N, sizeof...(Is), Is...>,
IList<sizeof...(Is), Is..., N>>::value
从C ++ 14开始,可以使用std::index_sequence
而不是IList
。
所以Example
可以写成
template <unsigned int N>
struct Example
{
template <unsigned int ... Is>
auto Function (Example<Is> ...)
-> typename std::enable_if<
std::is_same<IList<N, sizeof...(Is), Is...>,
IList<sizeof...(Is), Is..., N>>::value>::type
{ /* do something */ }
};
以下是使用示例(但请记住包括<type_traits>
)
int main()
{
Example<1U> e1;
Example<2U> e2;
// e1.Function(); // error
e1.Function(Example<1>{}); // compile
//e1.Function(Example<1>{}, Example<1>{}); // error
// e2.Function(); // error
//e2.Function(Example<2>{}); // error
e2.Function(Example<2>{}, Example<2>{}); // compile
//e2.Function(Example<2>{}, Example<2>{}, Example<2>{}); // error
}