可变数量的参数,没有宏或初始化列表的相同特定类型

variable number of arguments, same specific type without macro or initializer-list

我正在尝试做类似于 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 });

(由于遗留或生成的代码,可能需要完全匹配 f() 的语法。...而且它—arguably—只是 "looks better"。)

但我找不到使可变参数模板工作的方法

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() 不用宏也能工作? g() 非常 接近(只有一个函数,没有模板魔术),但是有那个宏 ...

如果你想要一堆 Foo,并且你想要允许 braced-init-lists,那么你应该这样做:

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

是的,这需要一套额外的牙套。不,您不应该使用宏来省略这两个字符。


您不能在此处使用可变参数模板,因为 braced-init-list 不是 表达式 并且它没有 type,所以不能推导。

{} 要求您正在初始化特定类型的内容。

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

live example.

现在,通过更改 to 100.

上方的数字 3,我们最多可以在 bobs 中进行 100 次重载

注意,如果你打了100多个,your compiler will die。这可以通过二叉树继承而不是线性继承来解决,但我不会被打扰。

此外,此技术会减慢编译速度。

注意Base必须是类型。您可以像上面那样使用 lambda 转发到您的模板函数(给它们不同的名称)、手动函数对象或其他任何东西。

如果不借助在调用中命名的类型(因此,使用 ADL 查找生成的函数),我无法解决使用此技术创建函数而不是函数对象的问题。函数对象不像函数那样参与重载决议,这可能是个问题。

添加额外的一组 {}

似乎也需要做很多工作