可变数量的参数,没有宏或初始化列表的相同特定类型 [英] variable number of arguments, same specific type without macro or initializer-list

查看:104
本文介绍了可变数量的参数,没有宏或初始化列表的相同特定类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试类似于 C ++ 11个可变数量的参数,相同的特定类型,但是我有自己的类型:

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<>{}语法一起使用(如建议的此处):

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()的语法....并且它-

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

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

h()h(1)h(1, 2)可以工作,但是h(1, {2, 3})不能编译,因为编译器无法像f()g_()那样找出{2, 3}的类型. /p>

有没有一种方法可以使f()在没有多个重载的情况下工作?还是让g()在没有宏的情况下工作? g()非常接近 (只有一个函数,没有模板魔术),但是有一个宏...

{}要求您初始化某种特定类型的东西.

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

实时示例.

现在,通过更改100 .

请注意,如果您的命中率超过100,您的编译器将会死亡 .可以通过二叉树继承而不是线性继承来解决此问题,但是我不为所动.

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

请注意,Base必须是一种类型.您可以使用上面的lambda来转发模板函数(给它们提供不同的名称),手动函数对象或其他任何东西.

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

似乎还需要做很多工作,才能添加额外的{}集.

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 ...

解决方案

{} 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 {}.

这篇关于可变数量的参数,没有宏或初始化列表的相同特定类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆