Variable number of arguments, the same specific type without a macro or list of initializers

I'm trying to do something similar to C ++ 11 with a variable number of arguments of the same specific type , but I have my own type:

struct Foo { Foo(int) {} Foo(int, int) {} }; 

with a bunch of overloads

 void f() {} void f(const Foo&) {} void f(const Foo&, const Foo&) {} // etc. ... as many f() overloads as needed ... 

works as desired: f(); f(1); f(1, 2); f(1, { 2, 3 }); f(); f(1); f(1, 2); f(1, { 2, 3 }); .

Instead of overloading, I can also use std::initializer_list<> with the syntax {} (as suggested here ):

 void g_(std::initializer_list<Foo>) {} g_({}); g_({ 1 }); g_({ 1, 2 }); g_({ 1, { 2, 3 } }); 

but it has an extra set of {} (yes, it's just two characters). To exactly match the f() syntax, use a macro:

 #define g(...) g_({__VA_ARGS__}) g(); g(1); g(1, 2); g(1,{ 2, 3 }); 

(Matching the syntax f() may be required precisely because of the outdated or generated code .... and it- possibly - just "looks better.")

But I can’t find a way to work with the variation template

 void h() { } template<typename... T> void h(const Foo&, T...) { } 

h() , h(1) and h(1, 2) work, but h(1, {2, 3}) cannot be compiled because the compiler cannot determine the type {2, 3} as it can, with using f() and g_() .

Is there a way to make f() without multiple overloads? Or for g() to work without a macro? g() very close (only one function and template magic), but there is a macro ...

+1
c ++ c ++ 11 initializer-list variadic-functions
24 Jan '17 at 2:53 on
source share
2 answers

{} requires you to initialize something of a certain type.

C ++ 11 variable arguments require your types to be subtracted.

These are opposite requirements.

Now I could generate an object with a set of overloads () to some large finite number.

 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}); } 

living example .

Now we can have up to 100 overloads in bobs , changing the number 3 above to 100 .

Note that if you press more than 100, your compiler will die . This can be fixed with inheritance of the binary tree instead of the linear one, but I cannot worry.

In addition, this method may slow down compilation.

Note that Base must be a type. You can use lambda as described above to go to your template function (give them different names), a manual function object, or something else.

Using this method to create a function instead of a function object is not something that I can solve without using the type named in the call (so use ADL to search for the generated function). Function objects do not participate in overload resolution in the same way as functions do, which can be a problem.

It also seems like a lot of work to do away with adding an extra set of {} .

+4
Jan 24 '17 at 18:45
source share

If you need the Foo s group, and you want to allow copied-init lists, then you should do:

 void foo(std::initializer_list<Foo> ); 

Yes, this requires one additional set of curly braces. No, you should not use a macro to omit these two characters.




You cannot use the variational pattern here because the bit-init-list is not an expression and it has no type, so it cannot be deduced.

+1
Jan 24 '17 at 15:07
source share



All Articles