私はコンパイラがインライン化したいことを行うことはできませんこれは、ハード。起こることはないだろう
。
あなたはランタイムポリモーフィズムを使用しています。定義では、コンパイラは、呼び出し時に呼び出されますどの機能を知ることができません。あなたは、それを手動で行うか、またはコンパイラにそれをさせるかにかかわらず、仮想ディスパッチのために支払うつもりです。
アブソリュートは、メンバー関数の呼び出し時に得られます。それはまだ「インライン」の部分に到達するために、メモリアクセス(「タイプ」をフェッチ)に基づいて条件分岐を行う必要があります。そして、あなたが追加したすべての新しい「状態」は、そのブランチに別条件を追加します。せいぜい、これは状態テーブルになります。これは仮想関数ポインタと同じでもありません。メモリアドレスからフェッチし、それを使用して特定のコードに分岐します。
ちょうどvtableポインタのように、あなただけがあなたの時間を無駄にして、コンパイラがあなたのためにできることを実装しました。
profile私はあなたの手書きの方法がコンパイラに勝つことができると仮定するのではなく、これを強くお勧めします。
あなたがあなたの代わりにboost.variant
し、適切な訪問者を使用する必要があり、言語レベルの多型を放棄することを決定した場合。あなたのコードは次のようになります。
typedef boost::variant<A, B> Wrapper;
struct FooVisitor : public boost::static_visitor<>
{
template <typename T> void operator()(T &t) {t.foo()};
};
あなたが呼び出したい機能毎FooVisitor
を行う必要があります。それを呼び出すには、次の操作を行います。
Wrapper a = existingA;
boost::apply_visitor(FooVisitor(), a);
を明らかに、あなたは簡単な関数でそれをラップすることができます
template<typename Visitor>
void Call(Wrapper &a) {boost::apply_visitor(Visitor(), a);}
:
void CallFoo(Wrapper &a) {boost::apply_visitor(FooVisitor(), a);}
確かに、あなたはこれらのテンプレート全体の家族を作ることができます
パラメータの受け渡しは許可されていません(訪問者自身にパラメータを格納する必要があります)が、戻り値を持つことができます(戻り値のタイプはboost::static_visitor<Typename_Here>
visiの宣言に入れなければなりません)。トル)。
また、boost::variant
オブジェクトには値のセマンティクスがあるため、コピーは内部オブジェクトをコピーします。また、boost::get()
の構文を使用して実際のタイプを取得することもできますが、が本当にに必要な場合を除き、私はそれを提案しません。ただ訪問者を使用してください。
「すべてのケースを記述するのが面倒だ/エラーが発生しやすい」ということです。言語機能を破棄して、自分で直接コーディングすることを選択すると、そのようになります。だからこそ、これらの言語機能が最初に存在するのです。誰かが定型文を書いてうんざりしていたからです。 –
追加するには、これは仮想ディスパッチを使用するよりもどのように優れていますか?これがif-elseチェーンではなくインデックス(配列参照など)で最適化されていても、これは(ほとんどのコンパイラの実装に関して)仮想ディスパッチを使用するのと同じです。つまり、STATE_Xはvtableを表し、Wrapper :: fooはvtableのlookupと似ています... –
実際に仮想関数を使用して簡単な実装を測定しましたか? 3-5「if」条件が仮想ファンクションコールのコストと容易に一致する可能性があります。そして、関数の成長が十分に大きい場合、インラインが実際に起こるという保証はありません。 –