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>包括#include <boost/signals2/signal.hpp> 哪个包括#include <boost/signals2/slot.hpp>哪个包括#include <boost/bind.hpp>哪个包括#include <boost/bind/bind.hpp>哪个包括在全局命名空间include <boost/bind/placeholders.hpp>中使用static boost::arg<1> _1;*,因此有歧义。

*:从技术上讲,_1它位于未命名的命名空间中,但由于 using 指令而可见。

一种解决方法是在文件顶部定义以下内容,以便<boost/bind/placeholders.hpp>不包括在内:

#define BOOST_BIND_NO_PLACEHOLDERS
于 2012-11-28T03:19:01.547 回答
14

C++ 看到两个名为_1. 它不知道您的意思是std::placeholders::_1代替 Boost 的_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

问题的提示可以在第二个错误中找到:boost::arg<1> {anonymous}::_1

原因是 boost 占位符位于全局命名空间内的匿名命名空间中。

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

由于 boost 占位符位于匿名命名空间中并且您正在导入std::placeholders到全局命名空间中,因此它们现在都可以在全局范围内使用。

因此,编译器无法知道您指的是哪个符号。

正如 Nicol 建议的那样,使用命名空间别名来创建速记前缀std::placeholders::_1以减少打字。

于 2012-11-28T03:19:18.200 回答
3

由于您使用的是std::bind我想您有一些 C++11 支持。也考虑这个版本(即更喜欢 lambdas over 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 回答
0

通过使用局部变量在范围内解决此问题的另一种方法:

{
    auto& _1 = std::placeholders::_1;
    auto f = std::bind(&Foo::bar, b, _1);
    ...
}
于 2017-06-16T10:10:36.937 回答