我正在尝试做类似C++11 variable number of arguments, same specific type的事情,但我有自己的类型:
struct Foo
{
Foo(int) {}
Foo(int, int) {}
};
带着一堆重载
void f() {}
void f(const Foo&) {}
void f(const Foo&, const Foo&) {}
// etc. ... as many f() overloads as needed ...
按需运作:f(); f(1); f(1, 2); f(1, { 2, 3 });
。
我还可以使用std::initializer_list<>
语法(建议here)来代替重载:
{}
但是有一组额外的void g_(std::initializer_list<Foo>) {}
g_({}); g_({ 1 }); g_({ 1, 2 }); g_({ 1, { 2, 3 } });
(是的,它只是两个字符)。要完全匹配{}
的语法,请使用宏:
f()
(由于遗留代码或生成的代码,可能需要完全匹配#define g(...) g_({__VA_ARGS__})
g(); g(1); g(1, 2); g(1,{ 2, 3 });
的语法....并且它 - arguably - 只是“看起来更好”。)
但是我找不到一种方法来使可变参数模板工作
f()
void h() { }
template<typename... T>
void h(const Foo&, T...) { }
,h()
和h(1)
工作,但h(1, 2)
无法编译,因为编译器无法确定h(1, {2, 3})
的类型,因为它可以使用{2, 3}
和f()
。
有没有办法让g_()
在没有多次重载的情况下工作?或者f()
没有宏工作? g()
非常关闭(只有一个函数,没有模板魔法),但是那个宏......
答案 0 :(得分:4)
{}
要求您初始化特定类型的内容。
C ++ 11变量参数要求您的类型被扣除类型。
这些是相反的要求。
现在我可以生成一个具有一组()
重载的对象,直到某个大的有限数。
namespace details {
template<std::size_t, class T>
using ignore_index=T;
template<class T, class Count, class Base>
struct linear_overload_count;
template<class T, std::size_t I0, std::size_t...Is, class Base>
struct linear_overload_count<T, std::index_sequence<I0,Is...>, Base>:
linear_overload_count<T, std::index_sequence<Is...>, Base>
{
using linear_overload_count<T, std::index_sequence<Is...>, Base>::operator();
using linear_overload_count<T, std::index_sequence<Is...>, Base>::linear_overload_count;
std::result_of_t<
Base const&(T const&, ignore_index<Is,T>const&...)
>
operator()(T const& t0, ignore_index<Is,T>const&...ts) const {
return Base::operator()(t0, ts...);
}
linear_overload_count()=default;
linear_overload_count(linear_overload_count const&)=default;
linear_overload_count(linear_overload_count &&)=default;
linear_overload_count& operator=(linear_overload_count const&)=default;
linear_overload_count& operator=(linear_overload_count &&)=default;
};
template<class T, class Base>
struct linear_overload_count<T, std::index_sequence<>, Base>:
Base
{
using Base::Base;
linear_overload_count(Base&& b):Base(std::move(b)) {}
linear_overload_count(Base const& b):Base(b) {}
std::result_of_t<
Base const&()
>
operator()() const {
return Base::operator()();
}
linear_overload_count()=default;
linear_overload_count(linear_overload_count const&)=default;
linear_overload_count(linear_overload_count &&)=default;
linear_overload_count& operator=(linear_overload_count const&)=default;
linear_overload_count& operator=(linear_overload_count &&)=default;
};
}
template<class T, std::size_t N, class Base>
using linear_overload_Ts = details::linear_overload_count<T, std::make_index_sequence<N>, Base>;
auto count_args_impl = [](auto&&...args) { std::cout << sizeof...(args) << "\n"; };
struct bob {
int x,y;
};
using count_bobs_t = linear_overload_Ts< bob, 3, decltype(count_args_impl) >;
count_bobs_t const bobs = count_args_impl;
int main() {
bobs();
bobs({}, {}, {1,2});
}
现在,我们可以通过更改to 100
上方的bobs
数量,在3
中最多包含100次重载。
请注意,如果您点击的数字超过100,your compiler will die。这可以用二叉树继承而不是线性树来修复,但我不能打扰。
此外,这种技术可能会减慢编译速度。
请注意,Base
必须是一种类型。您可以使用上面的lambda转发到模板函数(给它们不同的名称),手动函数对象或其他任何东西。
使用这种技术来创建函数而不是函数对象并不是我可以在没有调用中命名的类型的帮助下解决的问题(因此,使用ADL查找生成的函数)。函数对象不像函数一样参与重载解析,这可能是一个问题。
添加一组额外的{}
似乎还有很多工作要做。
答案 1 :(得分:1)
如果你想要一堆Foo
,并且想要允许braced-init-lists,那么你应该这样做:
void foo(std::initializer_list<Foo> );
是的,这需要一组额外的括号。不,您不应该使用宏来省略这两个字符。
您不能在此使用可变参数模板,因为braced-init-list不是表达式且它没有类型,因此它不能被推断出来。