2015-12-06 20 views
7

以下の例は、よく知られているイディオムの最小の、あまりよくない例です。
問題は熟語そのものについてではないので、最小限に保つにはコンパイルして醜いです。私は(そこに代替イディオムが存在する場合は、偶数か)可変長引数テンプレートメンバメソッドを紹介する時間が前にそれを変更する方法であるために苦しんだ何タイプ消去と可変テンプレートメンバー関数

struct Foo { 
    virtual void fn() = 0; 
}; 

template<class T> 
struct Bar: public Foo { 
    void fn() override { 
     T{}.fn(); 
    } 
}; 

struct S { 
    void fn() { } 
}; 

int main() { 
    Foo *foo = new Bar<S>{}; 
    foo->fn(); 
} 


明らかに、Fooクラスのfn関数を変更することはできません。これは仮想クラスであり、仮想指定子はテンプレートと一緒に使用されないためです。 fnの指定がBarの場合も同様です。なぜなら、ベースクラスの何らかの設定をオーバーライドする必要があるからです。

注意。私は強く、この質問は、今まで見た最大のXYProblemのいずれかになりますと思われるために

、私は、実際の問題の簡単な説明を与えることもしたいと思います。

Iは、2つのテンプレートメンバメソッドを公開するクラスを有する:

  • 最初のものではなく、後で使用するために何とかをを格納すべき直ちに使用されていないテンプレートクラスTを受け付け。

  • 2番目の引数は可変数の引数を受け取ります(実際には可変のテンプレート化されたメンバー関数です)。これらの引数は、新しく作成されたTのインスタンスに完全に転送される必要があります。

問題ははるかに複雑ですが、これはよく近似しており、目的が何であるかを知ることができます。

編集

私はそれが何らかの形で高階関数に類似していることと思います。
私が意味するところは、問題を解決するのは確かに最初の引数をバインドするためのテンプレート化された関数ですが、私が知っている限り、これまで不可能であったアプローチとこれまで検討してきた他のアプローチはありません。
同じ概念を表現する実行可能なソリューションはありますか?

私がコメントで述べた何
+0

あなたのより大きな目標は、実行時間とコンパイル時のダイナミズムを組み合わせることです。私は可能ではないと考えられる方法で、しかし、私は、私よりも鮮明な人々が何を思いつくのか興味があります。 – zwol

+0

@zwol私はまったく同じ感じを持っています。だから、私はSOに頼んだのです! :-) – skypjack

+0

間違いなく、*メンバ関数テンプレートは仮想ではありません* –

答えて

6

は、次のようなアプローチである。

ので
template<typename T> 
auto getFactory() { 

    return Factory<T>(); 
} 

auto factory=object.getFactory<someClass>(); 

// Then later: 

factory.construct(std::string("Foo"), bar()); // And so on... 

template<typename T> class Factory { 

public: 
    template<typename ...Args> 
    auto construct(Args && ...args) 
    { 
     return T(std::forward<Args>(args)...); 
    } 
}; 

だから今、あなたの最初の公開クラスメソッドは次のようなものになります

construct()の代わりにoperator()を使用することもできます。したがって、thの2番目の部分

factory(std::string("Foo"), bar()); // And so on... 

これは、実際にタイプ消去ではありません。ここでタイプ消去は使用できません。

これを数分考えれば、ここでタイプ消去を使用できない理由は、タイプ消去のインスタンスが「自己完結型」またはアトミックでなければならないためです。あなたがする必要があるのは、 2つの部分への原子タイプの消去、またはあなたの場合の2つのクラスメソッド。

これは動作しません。タイプ消去は、定義上、タイプを取り、それを「消去」します。最初の関数の型がクラスメソッドのテンプレートパラメータを消去すると、最終的には何らかの種類の不透明で型が消去されたオブジェクトになります。タイプ・イレースされたものは、もはや利用できなくなりました。しかし、あなたはまだ他の場所で発生しているあなたのコンストラクタパラメータをタイプ消去していません。

テンプレートクラスとコンストラクタパラメータを一緒にタイプイレーズすることができます。テンプレートクラスとコンストラクタのパラメータを別々にタイプイレーズしてから何らかの形で型を消去することはできません

私が概説したような単純な工場ベースのアプローチは、希望のタイプ消去の両方の半分が同じスコープ内に現れる場合は、タイプ消去と同様の結果に最も近づくでしょう。実際に型の消去を避けることができ、代わりにコンパイラが生成する肥大化に依存します。

+0

努力のためにUpvoted、とにかく私はそれが問題を解決するかどうかわからない。それ以外に、あなたは私に良いアイデアをくれたかもしれません。明日、私は行って、より多くの質問で戻ってきます。 – skypjack

+0

すでに言及したように、単純な理由で問題を解決することはできません。私は、 'Args ...'を取得するときに 'T'型が必要ですか、何とかできないタイプ消去して、タイプ消去してどこかに保存してください。 – skypjack

+0

C++では単純に不可能です。なぜなら、テンプレートは仮想的ではないからです。このようなことを行う唯一の方法は、クラスのコンストラクタが型消去されたパラメータパックを受け入れることです。次に、クラスの型を消去し、型消去されたパラメータパックを受け入れる型消去された仮想関数を提供し、次にコンストラクタを呼び出します。 –

4

ここでもあなたが望むものを正確に行うことはできません。私は、最も近い選択肢が(少なくともSamVarshavchikの回答とは異なる近い選択肢である)と私が思うものを投稿します。
私はあなたの問題を正確に解決するためにこの答えを期待していませんが、うまくいけばそれはあなたにいくつかのアイデアを与えるでしょう。

struct Delay // I have no idea what to call this 
    { 
     template <class T> 
     void SetT() 
     { 
      function_ = [](boost::any params){return T(params);} 
     } 

     template <class ... Args> 
     boost::any GetT(Args ... args) 
     { 
      return function_(std::make_tuple(args...)); 
     } 

     private: 
     std::function<boost::any(boost::any)> function_; 
    }; 

本の明白な制限はGetTを呼び出して、誰もが何らかの形でそれが助け場合は、そのクラスのtype_infoためboost::anyオブジェクトを照会することができるものの、Tはすでに何であったかを知っている必要がありますということです。もう1つの制限は、のオブジェクトを受け取り、boost::anyというオブジェクトを渡して、それをどうするかを知っていることです。

template <class F> 
    SetTFactory(F f) 
    { 
     function_ = f; 
    } 

し、それが好きで使用します:あなたはTは、このような場合、あなたはSetTを変更(または新しいメンバ関数を作成する)ことができることを行うことはできません場合は

Delay d; 
    d.SetTFactory([](boost::any s){return std::string(boost::any_cast<const char*>(s));}); 
    auto s = d.GetT("Message"); 
    assert(s.type() == typeid(std::string)); 

もちろん、これは全体を紹介します対処する新しい一連の難しさがあるので、私はこのソリューションがあなたにとってどれほど実用的であるか分かりません。私は何に関わらず、あなたのデザインをかなり考え直さなければならないと思います。

+0

Pretty tricky indeed。ありがとうございました。 – skypjack

関連する問題