Подтвердить что ты не робот

С++ 11 заполнителей с повышением

Этот код...

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

Ответ 1

Посмотрите, как работают функции include:

#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 находится в неназванном пространстве имен, но это видно из-за использования директивы.

Одним из способов является определение следующего в верхней части файла, чтобы <boost/bind/placeholders.hpp> не включался:

#define BOOST_BIND_NO_PLACEHOLDERS

Ответ 2

С++ видит два глобальных идентификатора с именем _1. Он не подозревает, что вы имеете в виду std::placeholders::_1 вместо Boost _1. Это одна из причин, по которой стандартная библиотека помещает их во вложенное пространство имен: чтобы предотвратить случайные конфликты, подобные этому.

Если вам нужно, чтобы они были короче, просто создайте простой псевдоним пространства имен:

namespace ph = std::placeholders

Тогда это просто ph::_1.

Ответ 3

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

Причина в том, что заполнитель-заполнители форматирования находятся в анонимном пространстве имен в глобальном пространстве имен.

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

Так как заполнители форматирования находятся в анонимном пространстве имен и вы импортируете std::placeholders в глобальное пространство имен, теперь они доступны в глобальной области.

Таким образом, компилятор не знает, к какому символу вы обращаетесь.

Как рекомендует Nicol, используйте псевдоним пространства имен, чтобы создать сокращенный префикс для std::placeholders::_1, чтобы уменьшить типизацию.

Ответ 4

Поскольку вы используете std::bind, я полагаю, что у вас есть поддержка С++ 11. Рассмотрим также эту версию (т.е. Предпочитайте lambdas над 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);
}

Ответ 5

Другим обходным путем для этого в пределах области с помощью локальной переменной:

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