C++11 个带提升的占位符

C++11 placeholders with boost

本文关键字:占位符 C++11      更新时间:2023-10-16

这段代码...

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.5 --stdlib=libc++ -std=c++0x 2 进行编译,整个代码块都是这个......

#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);
}

让我们看看包含是如何工作的:

#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

C++看到两个名为 _1 的全局标识符。它不知道你的意思是std::placeholders::_1而不是 Boost 的_1.这就是标准库将它们放在嵌套命名空间中的原因之一:以防止此类意外冲突。

如果需要它们更短,只需创建一个简单的命名空间别名:

namespace ph = std::placeholders

然后就ph::_1

.

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以减少类型。

既然你正在使用std::bind我想你有一些 C++11 支持。也考虑这个版本(即更喜欢 lambda 而不是 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);
}

使用局部变量在作用域内解决此问题的另一种解决方法:

{
    auto& _1 = std::placeholders::_1;
    auto f = std::bind(&Foo::bar, b, _1);
    ...
}