Передача переменной из типа std :: function завершается неудачно, хотя вызывающая сторона ожидает точно такой же тип - PullRequest
1 голос
/ 18 февраля 2020

по какой-то причине я не могу передать значение методу с точным типом.

Но если я приведу переменную, которую я sh передам, используя decltype, она каким-то образом сработает (?) Возможно, кто-нибудь знает, в чем причина.

Также, когда я передаю лямбда-блок в std :: function, он не требует приведения, хотя они не совсем одинаковые.

следующий пример демонстрирует мои выводы:

#include <functional>

class A {
public:
    A(std::function<void(std::string&, int)> &&f): _f(f) { }

    std::function<void(std::string&,int)> _f;
};

class B : public A {
public:
    //B(std::function<void(std::string&, int)> &&f) : A(f) { } --> fail on casting error, why ?
    B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }
};


class C {
public:
    C(std::function<void(std::string&,int)> f) {
        //b = new B(f); --> this one trigger casting error, why ? 
        b = new B((decltype(f)) f);
    }
    B *b;
};

int main(int argc, const char * argv[]) {
    // 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);
    });
}

Ответы [ 4 ]

3 голосов
/ 18 февраля 2020

Если вы получили rvalue ref и хотите передать этот параметр в качестве параметра для любой вызываемой вами функции, вы должны использовать std::move, чтобы сохранить его в качестве rvalue ref.

Объяснение, почему и как * Необходимо использовать 1004 *, можете ли вы найти здесь

Кстати: я понятия не имею, зачем вам нужно переоценивать ref в ваших функциях / конструкторах, но это то, что может быть полезно в вашем реальный код.

Ваш код будет примерно таким:

class A {
public:
    A(std::function<void(std::string&, int)> &&f): _f(std::move(f)) { }

    std::function<void(std::string&,int)> _f;
};

class B : public A {
public:
    B(std::function<void(std::string&, int)> &&f) : A(std::move(f)) { }
};


class C {
public:
    // ATTENTION: Here you use not an rvalue ref, so you create a copy
    // which later will be moved. Can be ok, dependent on what you want
    // to achieve
    C(std::function<void(std::string&,int)> f):b( new B(std::move(f))) { }
    B *b;
};

int main() {
    C c([=](std::string&a, int b) {
        printf("%s %d\n", a.c_str(), b);
    });
}
3 голосов
/ 18 февраля 2020

in: B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }

, поскольку f имеет имя, это lvalue , но конструктор A ожидает rvalue .

(decltype(f)) f здесь эквивалентно std::move(f).

1 голос
/ 18 февраля 2020

// это работает по какой-то причине, даже если C c ожидают std :: function ... Почему?

C c([=](std::string&a, int b) {
       printf("%s %d\n", a.c_str(), b);
    });

Поскольку конструкторы не explicit, компилятор ищет конструктор, в который переданное значение может быть неявно преобразовано. Таким образом, переданное значение, то есть лямбда, можно преобразовать в std::function через один из std::function конструкторов .

//b = new B(f); --> this one trigger casting error, why ? 
b = new B((decltype(f)) f);
Конструктор

B ожидает значение r, но значение f является значением l. Это вызывает ошибку. Однако приведение создает временную, то есть копию, которая является r-значением.

//B(std::function<void(std::string&, int)> &&f) : A(f) { } --> fail on casting error, why ?
B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }

Хотя f связан с r-значением, сам f, так как он имеет имя это lvalue. Приведение его к rvalue (decltype(f)) f)) преобразует его обратно в rvalue. Это то, что std::move делает.

1 голос
/ 18 февраля 2020

Проблема в том, что конструкторы 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 (при условии, что имеет место удаление копии, в противном случае копия этого временного объекта передается в конструктор).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...