2017-07-05 15 views
1

リスト内のすべてのクラスで特定の関数を呼び出すユーティリティクラスを作成しようとしています。この背後にある目的は、クラス階層内のリフレクションの要素を自動化することです。バリデーションテンプレートの再帰的テンプレート関数の終了の問題

Visual Studio 2015を使用してC++コードをコンパイルしていますが、コンパイラで再帰関数と終了関数を区別できないため、再帰的テンプレート関数が展開されたときにコンパイルエラーが発生します。私がしました

error C2668: 'Meta<C,B,A>::callOnAllClasses': ambiguous call to overloaded function 
could be 'void Meta<C,B,A>::callOnAllClasses<T,A,>(const T &)' 
or  'void Meta<C,B,A>::callOnAllClasses<T,A>(const T &)' 

:2015のVisual Studioでコンパイルされたときにこれは、次のエラーが発生し

#include <iostream> 

template< typename ... BaseClasses > 
class Meta 
{ 
public: 
    virtual ~Meta() {} 

    template< typename T > 
    void call(const T& val) 
    { 
     callOnAllClasses<T, BaseClasses...>(val); 
    } 

private: 
    template< typename T, typename HeadClass > 
    void callOnAllClasses(const T& val) 
    { 
     auto pObj = dynamic_cast<HeadClass*>(this); 
     if (pObj) 
      pObj->HeadClass::doSomething(val); 
    } 

    template< typename T, typename HeadClass, typename ... TailClasses > 
    void callOnAllClasses(const T& val) 
    { 
     auto pObj = dynamic_cast<HeadClass*>(this); 
     if (pObj) 
      pObj->HeadClass::doSomething(val); 
     callOnAllClasses<T, TailClasses...>(val); 
    } 
}; 

class A 
{ 
public: 
    void doSomething(int i) 
    { 
     std::cout << "A:" << i << std::endl; 
    } 
}; 

class B 
{ 
public: 
    void doSomething(int i) 
    { 
     std::cout << "B:" << i << std::endl; 
    } 
}; 

class C : public B, public A, public Meta<C,B,A> 
{ 
    public: 
    void doSomething(int i) 
    { 
     std::cout << "C:" << i << std::endl; 
    } 
}; 

int main() 
{ 
    C c; 
    c.call(5); 
} 

は、私は簡単なテストケースにクラスのコアを抽出しましたなぜかこれは間違っている可能性があります私は損失のビットで前にvariadicテンプレートを使用したことはありません。どんな助けでも大歓迎です!

+0

私は、これが問題の核心だと思うが、私は絶対にわからせずにお答えする必要はありません。 https://godbolt.org/g/G8WZbE。 mainを 'に置き換えると、それはコンパイルされます:https://godbolt.org/g/sXTCBE。 '-std = C++ 1z'と同じ結果になります。コンパイラは、適切なオーバーロードを選択できるように、型としてパラメータを渡す必要があると思います。 –

答えて

2

次のように問題を最小限に抑えることができます。

template< typename ... Bases > 
struct Meta 
{ 
    template< typename T > 
    void call(const T& val) 
    { 
     callOnAllClasses<T, Bases...>(val); 
    } 

    template< typename T, typename HeadClass > 
    void callOnAllClasses(const T& val) 
    { 

    } 

    template< typename T, typename HeadClass, typename ... TailClasses > 
    void callOnAllClasses(const T& val) 
    { 
     callOnAllClasses<T, TailClasses...>(val); 
    } 
}; 

struct C : Meta<int, int, int> { }; 

int main() 
{ 
    C{}.call(5); 
} 

TailClassesが空で、これらの両方の署名がコンパイラに曖昧であるとき:

template< typename T, typename HeadClass > 
void callOnAllClasses(const T& val); 

template< typename T, typename HeadClass, typename ... TailClasses > 
void callOnAllClasses(const T& val); 

助け再帰ケースに余分なテンプレート引数を追加しますTailClassesが空の場合、コンパイラはvariadicオーバーロードとnon-variadicオーバーロードの間で曖昧さを解消します。

template< typename T, typename HeadClass, typename T1, typename ... TailClasses > 
void callOnAllClasses(const T& val) 
{ 
    auto pObj = dynamic_cast<HeadClass*>(this); 
    if (pObj) 
     pObj->HeadClass::doSomething(val); 
    callOnAllClasses<T, T1, TailClasses...>(val); 
} 

live example on godbolt

+0

これはgccやclangでコンパイルされません。 –

+1

@MillieSmith:whoopsは私の答えを修正しました。 –

+0

おかげさまで@VittorioRomeo、これは完璧な意味合いで、私のより具体的なケースでも問題を解決します。私の混乱は、コンパイラが空の展開をどのように解釈したかにありました。空のリストが...引数にもマッチすることはわかりませんでしたが、今私はそれについて考えています。 –

関連する問題