С++ 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.
Ответы
Ответ 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
, содержащим вышеупомянутое значение