为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误

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

本文关键字:quot 调用 删除 错误 构造函数 复制 ostringstream 类型 成员 为什么      更新时间:2023-10-16

我已经隔离了一个问题"调用隐式删除的复制构造函数";在声明类的成员时使用ostrinstream类型的编译错误。在下面的示例中,定义了示例的Reading类的对象的STL列表。在调用push_back时,编译器搜索复制构造函数,但编译失败,这似乎是因为Readings的复制构造函数已被隐式删除。

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

我想我的问题可能是ostringstream是引用类型,正如这里所解释的:

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

Q。有人能证实我上面关于奥斯特林斯特流是导致问题的参考类型的假设是否正确吗?

我使用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;
}

每个类都有一个隐式声明的复制构造函数,如果您没有声明,但只有当每个数据成员和继承的类型都可以被复制构造时。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));

如果你选择实现复制构造函数/赋值,那么编译器将而不是为你生成移动构造函数/赋值。你必须明确地告诉编译器生成它们:

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

感谢cdhowie提供的详细而有用的答案。

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