Почему лямбда не может вернуть объект класса, если у класса есть явные конструкторы?

Я наткнулся на это любопытство и не понимаю, почему использование явных конструкторов может привести к сбою.

Я пытался генерировать и инициализировать объекты из данных конфигурации, используя лямбда-выражения. Я обнаружил, что лямбда-выражение может возвращать копию объекта только в том случае, если в определении класса объекта не используются явные конструкторы. Этот пример кода является упрощенным примером того, что я нашел.

class foo {
public:
    explicit foo() : _a(0) {}
    explicit foo(int val): _a(val) {}
    explicit foo(const foo& o) : _a(o._a) {}
    explicit foo(foo&& o) : _a(std::move(o._a)) {}
    foo& operator()(const foo& rhs) { if (this != &rhs) { _a = rhs._a; } return *this; }
    foo& operator()(foo&& rhs) { _a = std::move(rhs._a); return *this; }
    int a() const { return _a; }
    void a(int val) { _a = val; }
private:
    int _a;
};

auto makeFoo = [](int val) -> foo { return foo(val); };

Как написано, пример кода не компилируется со следующими ошибками в строке makeFoo:

In static member function ‘static foo<lambda(int)>::_FUN(int)’:
error: no matching function for call to ‘foo::foo(foo)’

Однако, если я удалю «явные» теги из конструкторов foo, код скомпилируется просто отлично.

Может ли кто-нибудь просветить меня, почему конструкторы не могут быть явными в этой лямбде?


person jp jamieson    schedule 06.04.2019    source источник
comment
совершенно уверен, что у вас будет такая же ошибка, но без использования лямбда с функцией normal   -  person Gian Paolo    schedule 06.04.2019
comment
Да, я только что проверил. К лямбде отношения не имеет. Все дело в том, как присваивается возвращаемое значение.   -  person jp jamieson    schedule 07.04.2019


Ответы (1)


Прежде всего, просмотрите документацию по ключевому слову explicit.

Указывает, что конструктор или функция преобразования (начиная с C++11) являются явными, то есть их нельзя использовать для неявных преобразований и инициализации копированием.

По сути, явный конструктор копирования означает, что конструктор копирования не будет вызываться неявно.

Вам не нужно определять свой конструктор копирования/перемещения, компилятор сгенерирует их за вас. То же самое для задания копирования/перемещения. Просто удалите их, и все будет хорошо.

class foo {
public:
    foo() = default;
    explicit foo(int val): _a(val) {}
    int a() const { return _a; }
    void a(int val) { _a = val; }
private:
    int _a{0};
};
person mohabouje    schedule 06.04.2019
comment
Я знаю, что компилятор создаст для меня конструкторы копирования/перемещения. Это не совсем та проблема, о которой я спрашивал. Это больше связано с тем, почему возвращаемое значение не работает, когда конструкторы являются явными. Я думаю, мне не приходило в голову, что возвращаемое значение по-прежнему присваивается, даже если в этой строке кода есть конструкция, а явные конструкторы копирования/перемещения предотвращают присваивание. Итак, в общем случае конструкторы копирования/перемещения не должны быть явными. Конструкторы инициализации могут быть. - person jp jamieson; 07.04.2019