Я попытался скомпилировать следующий пример, используя VC11 и g ++ 4.7.2:
#include <functional>
class X {
public:
template <typename T>
explicit X(T t)
{
std::bind(&X::invoke<T>, this, t)();
}
private:
template <typename T>
void invoke(T t)
{
t();
}
};
class Y {
public:
void foo() {
//...
}
};int main() {
Y y;
X x(std::bind(&Y::foo, &y));
return 0;
}
но это закончилось с ошибками. Я не уверен, разумно ли вставить весь вывод компилятора, но в целом
VC11 говорит:
ошибка C2664: ‘void std :: _ Pmf_wrap :: operator () (_ Farg0 &, _V0_t) const ‘: невозможно преобразовать параметр 3 из’ void ‘в’ std :: _ Bind, Y *, std :: _ Nil, STD :: _ NIL, STD :: _ NIL, STD :: _ NIL, STD :: _ NIL, STD :: _ Nil> ‘c: \ program files (x86) \ microsoft visual studio 11.0 \ vc \ include \ functions 1152 1 ConsoleApplication1 (компилятор Microsoft Visual C ++, ноябрь 2012 г., CTP)
и г ++:
Компиляция завершилась с ошибками:
source.cpp: в экземпляре ‘X :: X (T) [с T = std :: _ Bind (Y *)>]’:
source.cpp: 28: 33: требуется отсюда
source.cpp: 8: 9: ошибка: нет совпадения для вызова ‘(std :: _ Bind_helper (Y *)>), X * const, std :: _ Bind (Y *)>&> :: type {aka std :: _ Bind (Y *)>)> (X *, std :: _ Bind (Y *)>)>}) () ‘
Есть ли способ решить эту проблему. Для меня очень важно сохранить основную идею — класс, который может быть создан с помощью любого вызываемого объекта (объекта функции, указателя функции или оболочки вызова, возвращаемой std::bind()
функция).
Буду благодарен, если кто-то поможет.
Постскриптум Компилируется, если я создаю экземпляр X
, передавая объект функции или указатель на функцию.
Основной причиной проблемы, по-видимому, является внутреннее копирование аргументов, выполняемых std::bind
с особой ссылкой на t
,
Вы можете обойти это так:
template <typename T>
explicit X(T t)
{
std::bind(&X::invoke<T>, this, std::placeholders::_1)(t);
// ^^^^^^^^^^^^^^^^^^^^^ ^
}
Это также будет работать, но вы не сможете получить результат bind
пережить спор t
(в противном случае вы бы передавали висящую ссылку на invoke<T>()
):
template <typename T>
explicit X(T t)
{
std::bind(&X::invoke<T>, this, cref(t))();
// ^^^^^^^
}
ОБНОВИТЬ:
Вышеуказанные решения — это обходные пути, которые помогают достичь того, что вы показываете в своем примере. Однако из комментариев выяснилось, что ваш вариант использования может быть совершенно другим (например, передача результата bind
для дальнейшей оценки).
Как правильно указано Максим Егорушкин в его ответе концептуально правильное решение состоит в использовании такой конструкции, как Boost’s protect
,
Если вы не хотите использовать Boost, определить свой собственный довольно просто protect()
функция с использованием шаблонов переменных C ++ 11:
// Imitates boost::protect, but with variadic templates and perfect forwarding
namespace detail
{
template<typename F>
struct protect
{
private:
F _f;
public:
explicit protect(F f): _f(f)
{
}
template<typename... Ts>
auto operator () (Ts&&... args) ->
decltype(_f(std::forward<Ts>(args)...))
{
return _f(std::forward<Ts>(args)...);
}
};
}
template<typename F>
detail::protect<F> protect(F&& f)
{
return detail::protect<F>(std::forward<F>(f));
}
В конце концов, вот как вы могли бы использовать его в своем классе, как предположил Максим:
class X
{
public:
template <typename T>
explicit X(T t)
{
auto pt = protect(t);
std::bind(&X::invoke<decltype(pt)>, this, pt)();
}
private:
template <typename T>
void invoke(T t)
{
t();
}
};
Я думаю, что они пропустили важную часть boost::bind
при принятии его в std::bind
а именно boost::protect()
, Ваш код может быть исправлен следующим образом:
#include <boost/bind/protect.hpp>
// ...
X x(boost::protect(std::bind(&Y::foo, &y)));
Или, альтернативно:
template <typename T>
explicit X(T t)
{
auto tt = boost::protect(t);
auto f = std::bind(&X::invoke<decltype(tt)>, this, tt);
f();
}
Увидеть http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.html
Хотя первый аргумент по умолчанию не оценивается, все остальные аргументы. Иногда необходимо не оценивать аргументы, следующие за первым, даже когда они вложенные, подвыражения связываются. Это может быть достигнуто с помощью другого функционального объекта protect, который маскирует тип так, чтобы bind не распознал и не оценил его. При вызове protect просто перенаправляет список аргументов в другой объект функции без изменений.
Заголовок boost / bind / protect.hpp содержит реализацию protect. Чтобы защитить объект функции привязки от оценки, используйте protect (bind (f, …)).
Предстоящий Эффективный C ++ 11: контент и статус Скотт Мейерс собирается рекомендовать предпочитаю лямбды std :: bind. В C ++ 11 вы можете просто сделать:
template <typename T>
explicit X(T t)
{
auto f = [t, this]() { this->invoke(t); };
f();
}
// ...
X x([&y](){ y.foo(); });