假设我们有一个包装模板函数(或成员函数),如下所示:
template <class... T> void f(T... args) {
_f(args...);
}
void _f(int a1, ...); // variadic old style function
函数_f
(在我们无法修改的第三方库中定义)接受多种参数类型组合,并且希望在包装级别限制可能的参数。
如果我们有数百个这样的函数具有不同的可能参数类型,那么用命名参数手动定义重载函数会太笨重。最好使用带有可能类型列表的简单宏来定义这些函数。 Boost的宏允许迭代参数列表来构造命名参数看起来太重了
是否有一种优雅的方法来限制声明级别的可能参数?
答案 0 :(得分:2)
您可以使用特征根据其类型定义可接受的参数列表 它遵循一个最小的工作示例:
void _f(int, ...) {}
template<typename...> struct accepts;
template<> struct accepts<int, double, char> {};
template<> struct accepts<int, char, int> {};
template <class... T>
auto f(T... args) -> decltype(accepts<T...>{}, void()) {
_f(args...);
}
int main() {
f(0, 0., 'c');
f(0, 'c', 0);
// f(0, 0, 0);
}
最后一次调用会给你一个<int, int, int>
的编译时错误,它不是struct accepts
的有效特化。
这是因为未定义主模板,您可以根据需要引入越来越多的专业化来控制接受的列表。
以下是基于std::true_type
,std::false_type
和static_assert
的略有不同的解决方案:
#include<type_traits>
void _f(int, ...) {}
template<typename...> struct accepts: std::false_type {};
template<> struct accepts<int, double, char>: std::true_type {};
template<> struct accepts<int, char, int>: std::true_type {};
template <class... T>
void f(T... args) {
static_assert(accepts<T...>::value, "!");
_f(args...);
}
int main() {
f(0, 0., 'c');
f(0, 'c', 0);
// f(0, 0, 0);
}
优点是:
"!"
)std::false_type
并在需要时同时记录参数列表来明确禁用参数列表的可能性缺点是它有点冗长。
答案 1 :(得分:1)
好吧,hvd是正确的。你真正想要的是SFINAE。但是我已经开始用Boost.PP实现它了,所以... ...
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/comma_if.hpp>
#define DECL_PARAM(r, data, i, elem) \
BOOST_PP_COMMA_IF(i) elem _ ## i
#define FWD_PARAM(r, data, i, elem) \
BOOST_PP_COMMA_IF(i) _ ## i
#define DEF_FN_WRAPPER(name, delegate, params) \
void name(BOOST_PP_SEQ_FOR_EACH_I(DECL_PARAM, ~, params)) { \
delegate(BOOST_PP_SEQ_FOR_EACH_I(FWD_PARAM, ~, params)); \
}
扩展为:
void f( int _0 , double _1 , std::string _2 ) { _f( _0 , _1 , _2 ); }