2017-01-24 6 views
1

のない私は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 });

代わりのオーバーロード、私はまた、{}構文(hereが示唆したように)でstd::initializer_list<>を使用することができます。

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

それは{}の余分なセットを(ええ、それだけで2つの文字です)があります。 ...

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

(正確に理由はレガシーまたは生成されたコードで必要になることがありますf()の構文をマッチングし、IT関連はarguably -JUST「良く見える」:正確にf()の構文を一致させるために、マクロを使用します。 )

しかし、私は可変長テンプレート作業

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

h()h(1)h(1, 2)作品を作るための方法を見つけることはできませんが、h(1, {2, 3})はに失敗しましたコンパイラはの型をできるだけ正確に把握することができないため、コンパイルできません。f()g_()でコンパイルできます。

f()を複数の過負荷なしで動作させる方法はありますか?またはg()はマクロなしで動作しますか?あなたがFoo Sの束をしたい、とあなたはブレース-INIT-リストを許可したい場合は、あなたがすべき、g()非常に近い(一つだけの機能と鋳型なしマジック)ですが、そのマクロがあります...

+0

「複数の」過負荷にはどこに線を描いていますか? 2つのOKを持っているのですか? Variadicsはしばしば少なくとも2つ(再帰ケースと基底ケース)が必要な再帰に関連付けられています –

+0

@BenVoigt "unlimited";マクロのためでなければ、 'g()'が私の好みの解決策になります。 –

+1

'{2,3}'には型がないので、テンプレートの控除で推論することはできません。 – Jarod42

答えて

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

live example

bobsの数値を3より上に変更すると、最大で100個の過負荷が発生する可能性があります。to 100です。

あなたが数100を超える場合、your compiler will dieに注意してください。これは、線形の代りに二分木の継承で修正することができますが、私は気にすることはできません。

さらに、この手法ではコンパイルが遅くなる可能性があります。

Baseはタイプでなければならないことに注意してください。上のようなラムダを使用して、テンプレート関数(別の名前を付ける)、手動関数オブジェクト、その他のものに転送することができます。

関数オブジェクトの代わりに関数を作成するこの手法を使用すると、呼び出される型の助けがなければ解決できません(生成された関数を見つけるためにADLを使用します)。関数オブジェクトは、関数と同じ方法でオーバーロードの分解能に関与しません。これは問題の可能性があります。

これはまた、{}という特別なセットを追加することをやめる作業のようです。

+0

ニース...と** WOW **!ええ、本当に 'g _()'のシンプルさを示していますが、実際にはマクロなしで行うことができます。 –

+0

@ダン私はマクロを使用しません。私はむしろグローバルな検索とそのような99/100のケースでの交換をしたいと思います。 – Yakk

+0

引数なし... *実際には 'g_()'または 'f()'のどちらかを使用しますが、これは "うーん...私は疑問です"という質問でした。あなたは答えを出しました。 :-) –

1

do:

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

はい、これには追加の中かっこが1セット必要です。いいえ、マクロを使用して2つの文字を省略することはできません。ブレース-INIT-リストは表現ではありませんので、あなたがここに可変長引数テンプレートを使用することはできませんし、それがタイプを持っていないので、それは推測することはできません


+2

のどちらでもコンパイルされません。答えは "不可能、不可能"、 "しないでください"です。 –

+0

@ダンええ。テンプレートではできません。 – Barry

+0

「do not do that!」を具体的に詳述してください。一般的なマクロの悪さ以外は、構文を少しきれいにする良い方法のようです。 –

関連する問題