为什么 class 中的 ostringstream 类型的成员会导致 "call to implicity deleted copy-constructor" 错误?

Why does a member of ostringstream type in class cause "call to implicity deleted copy-constructor" error?

我已将“调用隐式删除的复制构造函数”编译错误问题与在声明 class 的成员时使用 ostringstream 类型隔离开来。在下面的示例中,定义了示例的 Reading class 对象的 STL 列表。在调用 push_back 的地方,编译器搜索复制构造函数并且编译失败,似乎是因为 Readings 的复制构造函数已被隐式删除。

当我注释掉引用 payloadString 的两行时,程序编译通过。

我在想我的问题可能是 ostringstream 是引用类型,如此处解释:

https://en.cppreference.com/w/cpp/language/copy_constructor “T 有一个右值引用类型的数据成员;”被引用为隐式删除复制构造函数的可能原因之一。

问。谁能确认我上面关于 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;
}

每个 class 都有一个 隐式声明的复制构造函数 如果你不声明一个,但前提是每个数据成员和继承类型都可以复制构造. std::ostringstream 没有复制构造函数,因此编译器无法为 Reading.

生成复制构造函数

您可以自己定义一个复制构造函数,如果您可以确定一种以有意义的方式构造 Reading::payloadString 的方法。例如,您可以执行以下操作:

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

请注意,这会复制 other.payloadString 中包含的字符串值,但不会复制流的其他方面,例如其各种输出模式或输出位置。这对你的情况可能就足够了。

如果你定义了这个构造函数,你可能还想定义复制赋值操作,同样的原因不能自动生成。您可以模仿上面复制构造函数的语义:

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));

如果您确实选择实现复制 constructor/assignment,那么编译器将 不会 为您生成移动 constructor/assignment,您必须明确地告诉编译器生成它们:

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

感谢 cdhowie 的详细而有用的回答。

我采纳了您的建议并使用 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.