Проблема в том, что конструкторы A
и B
ожидают ссылку на rvalue, пока вы передаете lvalue. Давайте рассмотрим конструктор B
:
B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }
Поскольку аргумент f
объявлен как ссылка на rvalue, он может привязываться только к rvalue на стороне вызывающей стороны. Другими словами, передача lvalue в качестве аргумента конструктору недопустима.
Однако этот аргумент в контексте тела функции (включая список инициализатора конструктора) f
является lvalue. Это означает, что он не может быть напрямую передан конструктору A
(который также ожидает значение r). То, что вы делаете с помощью (decltype(f))
, - это приведение f
к значению rvalue. Более обычный способ сделать это - использовать std::move
:
B(std::function<void(std::string&, int)> &&f) : A(std::move(f)) { }
. Вызов std::move
приводит к преобразованию f
lvalue в ссылку на rvalue без копирования.
В других местах , у вас та же проблема - вы пытаетесь передать lvalue в качестве аргумента rvalue. Используйте std::move
, чтобы исправить это.
Теперь к этой части:
// this works for some reason, even though C c'tor expect std::function ... why ?
C c([=](std::string&a, int b) {
printf("%s %d\n", a.c_str(), b);
});
Это работает, потому что std::function
имеет неявный конструктор преобразования из функционального объекта, который является лямбда функция есть. Этот код неявно создает временный объект std::function<void(std::string&, int)>
, который копирует лямбду во внутреннее хранилище. Затем этот временный объект передается конструктору C
(при условии, что имеет место удаление копии, в противном случае копия этого временного объекта передается в конструктор).