【问题标题】:Why does a member of ostringstream type in class cause "call to implicity deleted copy-constructor" error?为什么类中字符串流类型的成员会导致“调用隐式删除的复制构造函数”错误?
【发布时间】:2020-07-10 00:58:14
【问题描述】:

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

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

我认为我的问题可能是 ostringstream 是引用类型,如下所述:

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

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

【问题讨论】:

  • 流不可复制。因此,如果您有一个流作为成员,则默认复制构造函数不起作用,因为它会尝试复制所有类成员。

标签: c++ reference ostringstream default-copy-constructor


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    感谢 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.
    

    【讨论】:

      猜你喜欢
      • 2015-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多