Как связать шаблон функции-члена, передавая неопределенную оболочку вызова

Я попытался скомпилировать следующий пример, используя 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, передавая объект функции или указатель на функцию.

7

Решение

Основной причиной проблемы, по-видимому, является внутреннее копирование аргументов, выполняемых 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();
}
};
2

Другие решения

Я думаю, что они пропустили важную часть 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(); });
4

По вопросам рекламы [email protected]