20

このコード...

int main()
{
    using namespace std::placeholders;
    ClassA a;
    ClassB b, b2;
    a.SigA.connect( std::bind(&ClassB::PrintFoo, &b) );
    a.SigB.connect( std::bind(&ClassB::PrintInt, b,  _1));
    a.SigB.connect( std::bind(&ClassB::PrintInt, &b2, _1));

    a.SigA();
    a.SigB(4);
}

「エラー: '_1' への参照があいまいです」というコンパイル エラーが表示されます。

プレースホルダーを完全に修飾することで修正できます...

int main()
{
    // using namespace std::placeholders;
    ClassA a;
    ClassB b, b2;
    a.SigA.connect( std::bind(&ClassB::PrintFoo, &b) );
    a.SigB.connect( std::bind(&ClassB::PrintInt, b,  std::placeholders::_1));
    a.SigB.connect( std::bind(&ClassB::PrintInt, &b2, std::placeholders::_1));

    a.SigA();
    a.SigB(4);
}

...しかし、最初のコード スニペットが機能しないのはなぜでしょうか?

編集

あいまいさを避けるために、私はClangとBoost 1.52でコンパイルして--stdlib=libc++ -std=c++0xいます.コードブロック全体はこれです...

#include <boost/signals2.hpp>
#include <iostream>

struct ClassA
{
    boost::signals2::signal<void ()>    SigA;
    boost::signals2::signal<void (int)> SigB;
};

struct ClassB
{
    void PrintFoo()      { std::cout << "Foo" << std::endl; }
    void PrintInt(int i) { std::cout << "Bar: " << i << std::endl; }
};

int main()
{
    // using namespace std::placeholders;
    ClassA a;
    ClassB b, b2;
    a.SigA.connect( std::bind(&ClassB::PrintFoo, &b) );
    a.SigB.connect( std::bind(&ClassB::PrintInt, b,  std::placeholders::_1));
    a.SigB.connect( std::bind(&ClassB::PrintInt, &b2, std::placeholders::_1));

    a.SigA();
    a.SigB(4);
}
4

5 に答える 5

53

インクルードがどのように機能するか見てみましょう:

#include <boost/signals2.hpp>includes #include <boost/signals2/signal.hpp> which includes #include <boost/signals2/slot.hpp>which includes #include <boost/bind.hpp>which includes #include <boost/bind/bind.hpp>which includes 、グローバル名前空間で *include <boost/bind/placeholders.hpp>を使用しているため、あいまいです。static boost::arg<1> _1;

*: 技術的に_1は、名前のない名前空間にありますが、using ディレクティブにより表示されます。

回避策の 1 つは、ファイルの先頭に次のように定義して、<boost/bind/placeholders.hpp>含まれないようにすることです。

#define BOOST_BIND_NO_PLACEHOLDERS
于 2012-11-28T03:19:01.547 に答える
14

C++ は、 という名前の 2 つのグローバル識別子を認識し_1ます。std::placeholders::_1ブーストの代わりにあなたが意味することはわかりません_1。これは、標準ライブラリがそれらをネストされた名前空間に配置する理由の 1 つです: このような偶発的な競合を防ぐためです。

より短くする必要がある場合は、単純な名前空間エイリアスを作成するだけです:

namespace ph = std::placeholders

それならただph::_1です。

于 2012-11-28T03:10:25.433 に答える
4

GCC は、エラーに関する次の情報を提供します。

.../include/c++/4.7.0/functional:864:34: \
    error: candidates are: const std::_Placeholder<1> std::placeholders::_1
.../boost/1.49.0/boost/bind/placeholders.hpp:55:15: \
    error:                 boost::arg<1> {anonymous}::_1

問題のヒントは 2 番目のエラーにあります。boost::arg<1> {anonymous}::_1

原因は、ブースト プレースホルダーがグローバル名前空間内の匿名名前空間にあることです。

namespace
{
    boost::arg<1> _1;
    // etc...
} // unnamed namespace

ブースト プレースホルダーは匿名の名前空間にあり、グローバル名前空間にインポートstd::placeholdersしているため、両方ともグローバル スコープで使用できるようになりました。

そのため、コンパイラが参照しているシンボルを知る方法はありません。

Nicol が提案するように、名前空間のエイリアスを使用して省略形のプレフィックスを作成しstd::placeholders::_1、入力を減らします。

于 2012-11-28T03:19:18.200 に答える
3

あなたが使用しているのでstd::bind、C++ 11のサポートがあると思います。このバージョンも検討してください(つまり、よりラムダを好むstd::bind

int main()
{
    ClassA a;
    ClassB b, b2;
    a.SigA.connect( [&](){ b.PrintFoo(); } );
    a.SigB.connect( [&](int i){ b.PrintInt(i); } );
    a.SigB.connect( [&](int i){ b2.PrintInt(i); } );

    a.SigA();
    a.SigB(4);
}
于 2012-11-28T07:30:24.077 に答える