1

私の質問は、C ++の関数ラッパーへのインライン最適化の適用に関するものです。次のコードを検討してください。WorkerStructureオブジェクトは、機能の一部をカプセル化する関数ラッパーで初期化されます。関数ラッパーは、WorkerStructure::doSomeWorkメソッドが呼び出されたときに使用されます。

WorkerStructure :: doSomeWorkメソッドに適用すると、workerFunctionオブジェクトによってカプセル化された機能はインライン化されますか?明らかに、機能が他の変換ユニットで定義されている場合、workerFunctionオブジェクトは関数ポインターのみをカプセル化しますが、インライン化が行われる他の状況はありますか?不可能ですか?

別の変換単位で定義されたラムダ関数が関数ラッパーを介して渡される場合、それは関数ポインターを渡すことと実質的に同等ですか?

struct WorkerStructure
{
    WorkerStructure(std::function <bool(float)> &f):workerFunction(f) {}

    void doSomeWork(float inputValue)
    {
        if(workerFunction(inputValue))
        {
            //do some conditional operation
        }
    }
    std::function <bool(float)> workerFunction ;
};
4

2 に答える 2

5

の多形性により、実際にコールをインラインstd::function化することは非常に困難です。std::function缶は呼び出し可能なエンティティをストーリー化できるので、インライン化コードをどのように記述しますか?

これは、他の情報がない状態でベースポインターを介して呼び出される仮想関数をインライン化するようなものです(つまり、呼び出し前に派生からベースポインターに割り当てられず、コンパイラーがインライン化を有効にするために使用する場合があります)。

ほとんどの場合、実際の呼び出しやキャストなどを行う、テンプレート化された関数の特殊化へのポインターと関数ポインターを使用してstd::function実装されます。void*もちろん、これを行うために仮想関数を使用するバリアントもありますが、驚くほど難しい理由は、それらを使用することでより明確になります。リンク時の最適化でさえ何もできません。それは問題ではないので、あなたはすでにコールサイトで得ることができるすべての情報を持っています(それはそれほど多くはありません)。

これは、テンプレート関数バージョンへのポインターを使用する非常に大雑把なバージョンでありstd::function、ストアと呼び出しの側面のみを処理します(メモリ管理、コピー、移動、リセット、スペースの最適化などを除外します)。

template<class Sig>
class function;

template<class R, class... Args>
class function<R(Args...)>{
  typedef R (*call_type)(void*, Args...);
  void* _obj;
  call_type _caller;

public:
  template<class F>
  function(F f)
    : _obj(new F(f))
    , _caller([](void* p, Args... args){ return (*static_cast<F*>(p))(args...); })
  {}

  R operator()(Args... args) const{
    return _caller(_obj, args...);
  }
};

実例。_obj実際に何が入っているのか_caller、どこでfunction呼び出されているのかを確認するのは非常に難しいと思います。

参考までに、仮想関数を備えたバージョンを次に示します

于 2012-09-29T16:47:15.627 に答える
1

面白いことに、今日、メーリングリストでClang/LLVMの仮想関数のインライン化について質問しました。呼び出しは関数へのポインタにすぎないため、の動的な性質std::functionにより、本質的に仮想呼び出しになります。virtual

例としてLLVMを使用して、次のプログラムで遊んでみましょう。

#include <cstdio>

typedef void (*Function)();

void donothing() {}
void print() { printf("Hello World!"); }

Function get(int i) {
    if (i % 2 == 0) { return donothing; }
    return print;
}

int main() {
    Function f = get(0);
    f();
}

放出される主な機能:

define i32 @main() uwtable readnone {
  ret i32 0
}

したがって、コンパイラーは、どの関数が選択されるか(インライン化と定数伝​​搬の組み合わせを使用して)を理解し、呼び出しをインライン化することができます。

残念ながら、私は電子メールで、仮想テーブルを通過することは機能しないことを示しました(オプティマイザーはどういうわけか情報を失い、呼び出しをインライン化できませんでした)。したがって、インライン化が機能する可能性は完全にstd::functionありますが、コンパイラだけでなく、使用している特定の実装にも依存する可能性がありstd::functionます。アプリケーションを試してみる必要があります。

于 2012-09-29T17:57:52.687 に答える