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

С++ 1y нет жизнеспособного преобразования из std:: bind в std:: function

Я пытаюсь сохранить функцию forward в std::function. Если я использую std::bind, я получаю сообщение об ошибке, например no viable conversion from .... Если я использую лямбду, она скомпилируется.

Вот пример кода

#include <functional>

template<typename Handler>void func1(int a, Handler&& handler) {}
template<typename Handler>void func2(Handler&& handler)
{
    // this line compile fine
    std::function<void ()> funcA = [handler = std::move(handler)]() { func1(1, std::move(handler)); };
    // this line got compile error
    std::function<void ()> funcB = std::bind(func1<Handler>, 1, std::move(handler));
}

int main()
{
    func2(&main); // this just a sample, I am using functor as argument in real code
}

Попытка как g++ --std = С++ 1y (v4.9.0), так и clang++ --std = С++ 1y (v3.4.1) дает тот же результат

edit: сообщение об ошибке clang++

main.cpp:8:28: error: no viable conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int
      (*&&)()), int, int (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to
      'std::function<void ()>'
    std::function<void ()> funcB = std::bind(&func1<Handler>, 1, std::move(handler));
                           ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:14:5: note: in instantiation of function template specialization 'func2<int (*)()>' requested here
    func2(&main);
    ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2181:7: note: candidate constructor not viable: no
      known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
      (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'nullptr_t' for 1st argument
      function(nullptr_t) noexcept
      ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2192:7: note: candidate constructor not viable: no
      known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
      (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'const std::function<void ()> &'
      for 1st argument
      function(const function& __x);
      ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2201:7: note: candidate constructor not viable: no
      known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
      (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'std::function<void ()> &&' for
      1st argument
      function(function&& __x) : _Function_base()
      ^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2226:2: note: candidate template ignored:
      substitution failure [with _Functor = std::_Bind<void (*(int, int (*)()))(int, int (*&&)())>]: no matching function for call to object of
      type 'std::_Bind<void (*(int, int (*)()))(int, int (*&&)())>'
        function(_Functor);
        ^
1 error generated.
4b9b3361

Ответ 1

Введение

std:: bind попытается вызвать func1<Handler> с помощью ссылки lvalue, но ваше создание func1 сделает его только принятым rvalues.


ОБЪЯСНЕНИЕ

Здесь мы сократили ваш тестовый файл до минимума, чтобы показать, что происходит, нижеприведенный фрагмент плохо сформирован и объясняется, почему это так.

#include <functional>

template<class T>
void foobar (T&& val);

int main() {
  std::function<void()> f = std::bind (&foobar<int>, std::move (123));
}

В приведенном выше примере мы создадим экземпляр foobar с помощью T = int, что делает тип аргумента val ссылкой rvalue на int (int&&).

std::move(123) будет перемещать-построить наше значение, которое будет храниться внутри объекта, созданного std:: bind, но в стандарте указано, что когда std:: bind позже вызывает хранимую функцию, все аргументы передаются как TiD cv &; то есть. как lvalues.

Это поведение задано стандартом (n3797), как указано в разделе [func.bind.bind]p10.


Изменив предыдущий неверно сформированный фрагмент в следующем, ошибка не будет повышена, так как foobar<int> теперь принимает значение lvalue-reference; подходящий для привязки к lvalue, переданному нашей функции объектом-функцией, возвращаемым std:: bind.

  std::function<void()> f = std::bind (&foobar<int&>, std::move (123));

???

#include <functional>
#include <type_traits>
#include <iostream>

int main() {
  auto is_lvalue = [](auto&& x) {
    return std::is_lvalue_reference<decltype(x)> { };
  };

  auto check = std::bind (is_lvalue, std::move (123));
  bool res   = check (); // res = true
}

Ответ 2

: function должен быть скопирован. bind с rvalue возвращает не скопируемый объект. Обходным способом является захват/привязка с shared_ptr, содержащим вышеупомянутое значение