2017-01-20 6 views
0

コンパイラはシンボル_1をどのように解釈し、どのようにバインドが行われるのですか? 次の例を考えてみましょう:コールバックの_1としてのプレースホルダはどのように機能しますか?

class A { 
public: 
    boost::function<void (int x)> g; 
}; 

class B { 
public: 
    B() {} 
    static void foo(int i) { cout << "Hack: " << i <<endl; } 
}; 

int main() { 
    A a; 
    a.g = boost::bind(B::foo,_1); 
    a.g(2); 
    return 0; 
} 

何の魔法はラインboost::bind(B::foo,_1);に内部的にどうなりますか?

そして、次の行に渡された引数に_1がどのようにマップされていますか?a.g(2);

出力:
ハック:2

+0

C++ 11が追加されて以来、標準的なドキュメントについてはhttp://en.cppreference.com/w/cpp/utility/functional/placeholdersを参照してください。基本的には、バインドは解釈する方法を知っている特定の型と値です。 – clcto

+0

コンパイラは '_1'を名前として解釈します。これは' x'を解釈するのと同じです。その名前の実装はブーストライブラリにあります。これはオープンソースなので、自分で調べることができます。 –

+0

しかし、 '_1'が' a.g(2);で渡された引数にマップされている完全なメカニズムについて説明できます – Rama

答えて

0

私は私の能力を最大限に説明します。まず第一に、_1はグローバル変数にすぎません。これに関しては特別なことは何もなく、それ以外の名前も付けられています(placeholder1またはSergeyA)。しかし、_1のような名前は、短く、よく理解されている意味を持ち、_で始まり、プログラム内の他のグローバル名と衝突する可能性が低くなります。

魔法はこの変数のタイプです。それは生成されたbind*オブジェクトに反映される特別なタイプを持っています。後でoperator()が呼び出されると、型はoperator()引数から引数を取ると認識されます。

template<class F, class... ARG> 
struct bound { 
    bound(F f, ARGS&&... args) : bound_args(args...), functor(f) { } 
    std::tuple<ARG...> bound_args; 
    template<class... T> 
    void operator()(T&&... args); 

    F f; 
}; 

template<class F, class... T> 
auto bind(F f, T&& args) { 
    return bound<std::remove_reference_t<T>...>(f, args...); 
} 

それでは、プレースホルダの種類を紹介しましょう:正しいではなく、例示であり、擬似コードのような - ここ

は、C++を示すいくつかあります。

template<size_t N> 
struct placeholder { 
    enum { position = N; }; 
    template<class...T> 
    auto operator()(T&&... args) { 
     return std::get<position>(std::make_tuple(arg...)); 
    } 
}; 

placeholder<0> _1; 
placeholder<1> _2; 

これまでのところとても良いです。ここ

template<class... BOUND_ARGS> 
template<class... CALL_ARGS> 
void bound_object<BOUND_ARGS...>::operator() (CALL_ARGS&&... args) { 
    call_impl(args..., make_index_sequence<sizeof...(BOUND_ARGS)>{}); 
} 

make_index_sequenceは、関数の引数にタプル値を抽出するために必要とされているので、それにあまりにも多くの注意を払っていない:今、オペレータは()実際にバインドされたオブジェクトにどのように動作するかを見てみましょう。そしてここにcall_implがあります。

template<class... BOUND_ARGS> 
template<class... CALL_ARGS, size_t... ix> 
void bound_object<BOUND_ARGS...>::call_impl(CALL_ARGS&&... args, std::index_sequence<ix...>) { 
    f(to_arg().(std::get<ix>(bound_args), args...)...); 
} 

パズルの最後のピースto_argです:

template<class B, class... ARGS> 
auto to_arg(B&& b, ARGS... args) { 
    return b; 
} 

template<class... ARGS> 
auto to_arg(placeholder<0> p, ARGS&&... args) { 
    return p(args); 
} 

template<class... ARGS> 
auto to_arg(placeholder<1> p, ARGS&&... args) { 
    return p(args); 
} 

ここto_argの全体がバインド引数の型に基づいて、あなたにバインドされた引数または指定された引数のいずれかを与えることです。上記の例では、関数を部分的に特殊化することができるので3つのオーバーロードを使用しましたが、もちろんクラスに入れてクラスを部分的に特殊化する方が意味があります。

+0

うわー!どうもありがとう!今度は** Reflection **、** tuples **、 'make_index_sequence'について読む必要があります – Rama

+0

反射はここでは関係ありませんが、タプルはあります。 – SergeyA

関連する問題