1

I'm trying to do something similar to C++11 variable number of arguments, 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 });.

Instead of overloads, 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 that has an extra set of {} (yeah, it's just two characters). To match the syntax of f() exactly, use a macro:

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

(Matching the syntax of f() exactly might be needed because of legacy or generated code. ... and it—arguably—just "looks better".)

But I can't find a way to make a variadic template work

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

h(), h(1) and h(1, 2) work, but h(1, {2, 3}) fails to compile because the compiler can't figure out the type of {2, 3} as it can with f() and g_().

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

Community
  • 1
  • 1
Ðаn
  • 10,934
  • 11
  • 59
  • 95
  • Where do you draw the line on "multiple" overloads? Is having two ok? Variadics often are associated with recursion, where you need at least two (the recursion case and the base case) – Ben Voigt Jan 24 '17 at 14:58
  • 1
    `{2, 3}` has no type, and so cannot be deduced by template deduction. – Jarod42 Jan 24 '17 at 15:04
  • I cannot reproduce your problem, could you post a [MCVE](http://stackoverflow.com/help/mcve) please ? Also, which compiler are you using ? – cmourglia Jan 24 '17 at 15:06
  • Indeed this one does not compile either with gcc – cmourglia Jan 24 '17 at 16:18

2 Answers2

4

{} requires you are initializing something of a specific type.

C++11 variable arguments requires that your types be deducted types.

These are opposing requirements.

Now I could generate an object with a set of () overloads up 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});
}

live example.

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

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

In addition, this technique can slow down compiling.

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

Using this technique to create a function instead of a function object isn't something I can solve without the help of a type being named in the call (so, use ADL to find the generated function). Function objects don't participate in overload resolution in the same way that functions do, which may be a problem.

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

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

If you want a bunch of Foos, and you want to allow braced-init-lists, then you should do:

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

Yes, this requires one extra set of braces. No, you shouldn't use a macro to be able to omit those two characters.


You can't use a variadic template here because a braced-init-list is not an expression and it does not have a type, so it can't be deduced.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • @Dan Yeah. You just can't do it with a template. – Barry Jan 24 '17 at 15:11
  • 1
    @Dan "General macro badness" is a lot of badness. "Clean up the syntax a little bit" saves you *two characters*. Doesn't seem like a worthwhile tradeoff in the slightest. – Barry Jan 24 '17 at 15:14
  • This solution was stated by who asked, when cited: http://stackoverflow.com/questions/18017543/c11-variable-number-of-arguments-same-specific-type/ – Rama Jan 24 '17 at 15:55
  • 1
    I mean, I think @Dan is looking for an alternative solution, because he already cited that. – Rama Jan 24 '17 at 16:10
  • @Dan: If you'd like `g{ 1, {1,2} }` to work, which is no extra characters, just braces instead of parens, that's possible. – Ben Voigt Jan 24 '17 at 23:44
  • 1
    Another possible thing would be to have the bunch of overloads, but generated automatically by the compiler. – Ben Voigt Jan 24 '17 at 23:46