2016-05-09 2 views
1

バリデーションテンプレートへの私の最初のベンチャーでは、テンプレート引数のすべての組み合わせで派生クラスをインスタンス化する関数を作成しようとしています。実行時オプションクラスを使用するというアイデアは、使用する正確なテンプレートのインスタンス化を選択できることになります。私は1と2のテンプレート引数で動作するベースケースを得ることができました。しかし、一般的なケースが動作していない、以下SelectInstanceの第三の呼び出しは次のエラーでコンパイルに失敗します。ランタイム値をランタイム値をコンパイルテンプレート引数に変換する複数のパラメータパックの操作

candidate function not viable: requires 3 arguments, but 2 were provided AbstractBase *SelectInstance(Func func, Args... args)

完全なコード以下の通りです:

#include <memory> 
#include <iostream> 

struct Options 
{ 
    bool GetFirstParameter() { return true; } 
    bool GetSecondParameter() { return false; } 
    bool GetThirdParameter() { return true; } 
}; 

struct AbstractBase 
{ 
    virtual void PrintMe() = 0; 
}; 

template<class... Args> 
struct Derived : public AbstractBase 
{ 
    virtual void PrintMe() { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
}; 

template<class... Args> 
AbstractBase *SelectInstance() 
{ 
    return new Derived<Args...>(); 
} 

template<class Func, class... UnpackedArgs> 
AbstractBase *SelectInstance(Func func) 
{ 
    if (func()) 
    return SelectInstance<float, UnpackedArgs...>(); 
    else 
    return SelectInstance<double, UnpackedArgs...>(); 
} 

template<class Func, class... Args, class... UnpackedArgs> 
AbstractBase *SelectInstance(Func func, Args... args) 
{ 
    if (func()) 
    return SelectInstance<Args..., float, UnpackedArgs...>(args...); 
    else 
    return SelectInstance<Args..., double, UnpackedArgs...>(args...); 
} 

int main() 
{ 
    Options opts; 
    std::unique_ptr<AbstractBase> one(
    SelectInstance(
     std::bind(&Options::GetFirstParameter, &opts) 
    ) 
    ); 
    one->PrintMe(); 

    std::unique_ptr<AbstractBase> two(
    SelectInstance(
     std::bind(&Options::GetFirstParameter, &opts), 
     std::bind(&Options::GetSecondParameter, &opts) 
    ) 
    ); 
    two->PrintMe(); 

    // this one fails to compile! 
    std::unique_ptr<AbstractBase> three(
    SelectInstance(
     std::bind(&Options::GetFirstParameter, &opts), 
     std::bind(&Options::GetSecondParameter, &opts), 
     std::bind(&Options::GetThirdParameter, &opts) 
    ) 
    ); 
    three->PrintMe(); 
} 

私はおそらく可変長を悪用していますファンクタを新しいパラメータパックに変換するためのテンプレートが不適切です。どんな指導も高く評価されます。

答えて

1

あなたの試みが失敗する理由は、コンパイラが何を提供しているのかをコンパイラが知ることができないためです。わかりやすくするために、関数のタイプがA,B、およびCであるとします。最初の呼び出しは次のとおりです。ここから

// Func = A, Args... = {B, C}, UnpackedArgs... = {} 
AbstractBase *SelectInstance(Func func, Args... args) 

は、我々はSelectInstance<B, C, float>(b, c);あなたはそれが意味することを意図している、と言うことですSelectInstance<Args..., float, UnpackedArgs...>(args...);を呼び出す:

// Func = B, Args... = {C}, UnpackedArgs... = {float} 
AbstractBase *SelectInstance(Func func, Args... args) 

が、本当にパラメータパックは貪欲であり、すべてを取ります。 Args...は、明示的に指定するすべての後続の型引数を取ります。だから、本当にその呼び出しは次のように解釈されます。

// Func = B, Args... = {C, float}, UnpackedArgs... = {} 
AbstractBase *SelectInstance(Func func, Args... args) 

この関数は三つの引数(BC、およびfloat)を取るが、あなただけのエラー、したがって、2つ(bc)を渡しています。


最初演繹ものを置くために、あなたのテンプレート引数の順序を反転し、推定されたものを最後に、この問題を解決するには。この方法では、あなたはまだ正しいことを行うとしても、余分な過負荷を避けるために、テンプレートの控除を使用することができます。

template<class... Args> 
AbstractBase *SelectInstance() 
{ 
    return new Derived<Args...>(); 
} 

template<class... UnpackedArgs, class Func, class... Args> 
AbstractBase *SelectInstance(Func func, Args... args) 
{ 
    if (func()) { 
    return SelectInstance<float, UnpackedArgs...>(args...); 
    } 
    else { 
    return SelectInstance<double, UnpackedArgs...>(args...); 
    } 
} 

今、あなたが提供するすべての引数がFuncArgs...UnpackedArgs...と控除の数字に入るので、これは動作します - ですまさにあなたが望むものです。

+0

ありがとうございました。余分な過負荷を書いたら、間違った道を歩いているように感じました。これを "SelectInstance "に反転して、アンパックの順序を引数として渡されたファンクタと同じに保ちました。 –

関連する問題