С++ 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);
}
Ответы
Ответ 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);
...
}