Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 placeholders with boost

Tags:

c++

c++11

boost

This code ...

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

Gives the compilation error, "error: reference to '_1' is ambiguous"

It can be fixed by fully qualifying the placeholders ...

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

...but why doesn't the first code snippet work?

EDIT

Just to prevent any ambiguity, I am compiling with Clang and Boost 1.52 with --stdlib=libc++ -std=c++0x and the entire code block is this ...

#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);
}
like image 937
learnvst Avatar asked Nov 28 '12 02:11

learnvst


People also ask

What is_ 1 in c++?

The objects _1, _2, ... _N are placeholders that represent the first, second, ..., through Nth argument, respectively in a function call to an object returned by bind . For example, you use _6 to specify where the sixth argument should be inserted when the bind expression is evaluated.

What is _1 in boost :: bind?

_1 is a placeholder. Boost. Bind defines placeholders from _1 to _9 . These placeholders tell boost::bind() to return a function object that expects as many parameters as the placeholder with the greatest number.

What does boost :: bind do?

boost::bind is a generalization of the standard functions std::bind1st and std::bind2nd. It supports arbitrary function objects, functions, function pointers, and member function pointers, and is able to bind any argument to a specific value or route input arguments into arbitrary positions.


3 Answers

Let's see how the includes work:

#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>, which uses static boost::arg<1> _1;* in the global namespace, hence the ambiguity.

*: Technically, _1 is in an unnamed namespace, but it visible due to a using directive.

One workaround is define the following at the top of your file so that <boost/bind/placeholders.hpp> is not included:

#define BOOST_BIND_NO_PLACEHOLDERS
like image 131
Jesse Good Avatar answered Sep 30 '22 02:09

Jesse Good


C++ sees two global identifiers named _1. It has no idea that you mean std::placeholders::_1 instead of Boost's _1. This is one of the reasons why the standard library puts them in a nested namespace: to prevent accidental conflicts like this.

If you need them to be shorter, just create a simple namespace alias:

namespace ph = std::placeholders

Then it's just ph::_1.

like image 28
Nicol Bolas Avatar answered Sep 30 '22 00:09

Nicol Bolas


GCC give the following information regarding your error:

.../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

The hint to the problem can be found in the 2nd error: boost::arg<1> {anonymous}::_1

The cause is that the boost placeholders are in an anonymous namespace within the global namespace.

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

Since the boost placeholders are in an anonymous namespace and you're importing std::placeholders into the global namespace, they are now both available in the global scope.

As such there is no way for the compiler to know which symbol you are referring to.

As Nicol suggests, use a namespace alias to create a shorthand prefix to std::placeholders::_1 to reduce the typing.

like image 38
Steve Lorimer Avatar answered Sep 30 '22 00:09

Steve Lorimer