Почему член типа ostringstream в классе вызывает ошибку «вызов неявно удаленного конструктора копии»? - PullRequest
0 голосов
/ 10 июля 2020

Я изолировал проблему с ошибками компиляции «вызов неявно удаленного конструктора копирования» на использование типа ostringstream при объявлении членов класса. В приведенном ниже примере определен список объектов класса Reading в формате STL. В момент вызова push_back компилятор ищет конструктор копирования, и компиляция завершается неудачно, по-видимому, потому, что конструктор копирования для Readings был неявно удален.

Когда я закомментировал две строки, относящиеся к payloadString, программа компилируется.

Я думаю, что моя проблема может заключаться в том, что ostringstream имеет ссылочный тип, как описано здесь:

https://en.cppreference.com/w/cpp/language/copy_constructor "T имеет элемент данных ссылочного типа rvalue; " цитируется как одна из возможных причин неявного удаления конструкторов копирования.

Q's. Может ли кто-нибудь подтвердить правильность моего вышеупомянутого предположения о том, что ostringstream имеет ссылочный тип, вызывающий проблему?

Я использую ostringstream по причинам, которые не совсем очевидны в этом надуманном примере. Возможно, мне нужно найти другой способ обработки этой строки, но может ли кто-нибудь предложить подход, который здесь будет работать?

// testing a problem where ostringstream causes implicitly deleted copy constructor
//
// using ostringstream in a class definition seems to cause implicit deletion of the copy constructor

#include <iostream>
#include <sstream>
#include <list>
#include <string>

using namespace std;

class Reading {
    public:
        double elevation;
        std::ostringstream payloadString; // using ostringstream here causes implicit deletion of the copy constructor
        double speed;

    // constructors and member functions
        Reading();          // initialisation constructor declaration
    private:
    };

Reading::Reading(): // initialisation constructor definition
        elevation(0.0),
        payloadString("_null_null_"),  // commenting out this line and the previous definition in the class makes the problem go away
        speed(0.0)
        {}

int main()
{

    std::list<Reading> readingsList; // a list of readings

    Reading fakeReading; // just initialises with dummy data

    // this line is what causes the compiler to complain about implicitly deleted copy constructors
    readingsList.push_back(fakeReading);

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 10 июля 2020

Спасибо cdhow ie за ваш подробный и полезный ответ.

Я принял ваш совет и реализовал свой пример с помощью emplace_back. Следующий код, похоже, теперь работает довольно хорошо.

// testing a problem where ostringstream causes implicitly deleted copy constructor
//
// using ostringstream in a class definition seems to cause implicit deletion of the copy constructor

#include <iostream>
#include <sstream>
#include <list>
#include <string>

using namespace std;

class Reading {
    public:
        double elevation;
        std::ostringstream payloadString; // using ostringstream here causes implicit deletion of the copy constructor
        double speed;

    // constructors and member functions
        Reading();          // initialisation constructor declaration
    private:
    };

Reading::Reading(): // initialisation constructor definition
        elevation(0.0),
        payloadString("_null_null_"),  // commenting out this line and the previous definition in the class makes the problem go away
        speed(0.0)
        {}

int main()
{

    std::list<Reading> readingsList; // a list of readings

    Reading fakeReading1; // just initialises with dummy data
    Reading fakeReading2; // just initialises with dummy data
    Reading fakeReading3; // just initialises with dummy data

    fakeReading1.elevation = 1.0;
    fakeReading2.elevation = 2.0;
    fakeReading3.elevation = 4.0;

    fakeReading1.payloadString.str("reading1 payload");

    fakeReading3.payloadString.str("reading3 payload");

    // this line is what causes the compiler to complain about implicitly deleted copy constructors
    readingsList.emplace_back(std::move(fakeReading1));
    readingsList.emplace_back(std::move(fakeReading2));
    readingsList.emplace_back(std::move(fakeReading3));

 for (auto const &v : readingsList){
        cout << "elevation = " << v.elevation  << endl;
        cout << "speed = " << v.speed  << endl;
        cout << "payloadString = " << v.payloadString.str()  << endl << endl;
 }
    return 0;
}

Что дает следующий результат, правильно и как ожидалось:

elevation = 1
speed = 0
payloadString = reading1 payload

elevation = 2
speed = 0
payloadString = _null_null_

elevation = 4
speed = 0
payloadString = reading3 payload


Process returned 0 (0x0)   execution time : 0.023 s
Press any key to continue.
0 голосов
/ 10 июля 2020

Каждый класс имеет неявно объявленный конструктор копирования , если вы его не объявляете, но только если каждый член данных и унаследованный тип могут быть созданы копированием. std::ostringstream не имеет конструктора копирования, поэтому компилятор не может сгенерировать конструктор копирования для Reading.

Вы можете определить конструктор копирования самостоятельно, если вы можете определить способ построения Reading::payloadString осмысленным способом . Например, вы можете сделать что-то вроде:

Reading(Reading const & other) :
    elevation{other.elevation},
    payloadString{other.payloadString.str()},
    speed{other.speed} { }

Обратите внимание, что это копирует строковое значение, содержащееся в other.payloadString, но не копирует другие аспекты потока, такие как его различные режимы вывода или его положение вывода. . В вашем случае этого может быть достаточно.

Если вы определяете этот конструктор, вы также можете определить операцию копирования-присваивания, которая не может быть автоматически сгенерирована по той же причине. Вы можете имитировать c семантику конструктора копирования выше:

Reading & operator=(Reading const & other) {
    elevation = other.elevation;
    payloadString = std::ostringstream{other.payloadString.str()};
    speed = other.speed;
    return *this;
}

Обратите внимание, что std::ostringstream можно перемещать, , что означает, что компилятор автоматически сгенерирует конструктор перемещения и оператор присваивания перемещения для Reading. Следовательно, вы можете просто переместить-построить элемент списка из fakeReading:

readingsList.emplace_back(std::move(fakeReading));

Если вы решите реализовать конструктор / присваивание копирования, то компилятор не сгенерирует перемещение конструктор / назначение для вас, и вам нужно будет явно указать компилятору, чтобы они сгенерировали их:

Reading(Reading &&) = default;
Reading & operator=(Reading &&) = default;
...