10

I am trying to make a struct, in which one of the members is of std::stringstream type. I am using C++11, and according to http://www.cplusplus.com/reference/sstream/stringstream/operator=/ I can do it.

Here is my code:

struct logline_t
    {
        stringstream logString; /*!< String line to be saved to a file (and printed to cout). */
        ElogLevel logLevel; /*!< The \ref ElogLevel of this line. */
        timeval currentTime; /*!< time stamp of current log line */

        logline_t& operator =(const logline_t& a)
        {
            logString = a.logString;
            logLevel = a.logLevel;
            currentTime = a.currentTime;

            return *this;
        }
    };

It doesn't compile, as I am getting this error:

error: use of deleted function ‘std::basic_stringstream<char>& std::basic_stringstream<char>::operator=(const std::basic_stringstream<char>&)’

I don't understand why it doesn't work. I have tried logString = move(a.logString); as well. Same result. I would appreciate all help.

Edit: Here is my code, I have applied the changes suggested by most of the users and in my code they do not compile. I am still getting an error at the very beginning of the struct.

CLogger.h

Line 40: ../src/CLogger.h:40:9: error: use of deleted function ‘std::basic_stringstream<char>::basic_stringstream(const std::basic_stringstream<char>&)’

CLogger.cpp

Line 86: ../src/CLogger.cpp:86:41: error: use of deleted function ‘CLogger::logline_t::logline_t(const CLogger::logline_t&)’

Line 91: ../src/CLogger.cpp:91:9: error: use of deleted function ‘CLogger::logline_t::logline_t(const CLogger::logline_t&)’

If any other information is needed i will provide it.

Null
  • 1,950
  • 9
  • 30
  • 33
Łukasz Przeniosło
  • 2,725
  • 5
  • 38
  • 74
  • 4
    The link you posted contains all information needed: `copy (1) stringstream& operator= (const stringstream&) = delete;` – Anton Savin Jul 01 '15 at 09:40
  • How to solve this problem? `operator<<` doesnt work as well. @ildjarn i am using an arm crosscompiller `gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-12ubuntu1)` – Łukasz Przeniosło Jul 01 '15 at 09:41
  • 1
    `stringstream` is not copyable. If you actually do need to copy it, I guess you'll need to write an assignment operator that reads the contents of the stream and writes it to a new one. – Jonathan Potter Jul 01 '15 at 09:42
  • @JonathanPotter but I tried `logString << a.logString;` and it gives the same error. – Łukasz Przeniosło Jul 01 '15 at 09:44
  • 3
    streams are never copyable, and are also not movable with libstdc++ prior to 5.1. – ildjarn Jul 01 '15 at 09:55
  • @ŁukaszPrzeniosło, according t o all your comments below you are also getting a **different** error from the one in your question, which means you have a **different** problem. Stop complaining that answers to your question don't fix some other problem that you haven't shown. You are obviously trying to copy construct the type in addition to the code you have shown in your question Fix your question to show the real code. – Jonathan Wakely Jul 01 '15 at 11:13
  • I can post all my code, but that will only shadow the problem even more. I have updated my question with some additional info. – Łukasz Przeniosło Jul 01 '15 at 11:17
  • I have updated the answer. – Łukasz Przeniosło Jul 01 '15 at 19:32
  • Please don't post links to external sites. They'll go down eventually, rendering this question useless for furture readers. Ensure all relevant code is posted in the question body. –  Jul 02 '15 at 08:56

4 Answers4

12

std::stringstream is not copyable. To copy the content you can just write the content of one stream to another:

logString << a.logString.str();

Update: Also if you don't follow a good advice to implement operator= with copy-and-swap idiom using copy constructor, you have to clear the stream first:

logString.str({});
logString << a.logString.str();

or just

logString.str(a.logString.str());

Also you may be tempted to use rdbuf() instead:

logString << a.logString.rdbuf();

but this is incorrect, because it alters the state of a.logString (despite that a.logString is const, a.logString.rdbuf() is a pointer to non-const object). This is demonstrated by the following code:

logline_t l1;
l1.logString << "hello";
logline_t l2, l3;
l2 = l1;
l1.logString << "world";
l3 = l1;
// incorrectly outputs: l2: hello, l3: world
// correct output is: l2: hello, l3: helloworld
std::cout << "l2: " << l2.logString.str() << ", l3: " << l3.logString.str() << std::endl;
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
4

Streams are not copyable. But they are movable.

Still you could make your copy assignment operator work by just creating a suitable stringstream.

On the third hand it doesn't quite feel right to have a stream as a member? And if you really want that, why not use an ostringstream, why a stringstream? By reflecting on this, the design might be improved (and possibly that will the remove the current problem).


Example of workaround by creating a suitable stringstream (well, ostringstream), using the exception safe copy/swap idiom:

#include <sstream>
#include <utility>          // std::swap

namespace my {
    using std::ostringstream;
    using std::swap;

    struct S
    {
        ostringstream line;

        void swap_with( S& other ) noexcept
        {
            swap( line, other.line );
        }

        auto operator=( S const& other )
            -> S&
        {
            S temp( other );
            swap_with( temp );
            return *this;
        }

        S() = default;

        S( S const& other )
            : line( other.line.str() )
        {}
    };
}  // namespace my

auto main() -> int
{
    my::S o1, o2;
    o1 = o2;
}

Note that this relies on std::swap, which is specialized for ostringstream.


Example of a simpler but in principle not exception safe workaround:

#include <sstream>
#include <utility>          // std::swap

namespace my {
    using std::ostringstream;
    using std::swap;

    struct S
    {
        ostringstream line;

        auto operator=( S const& other )
            -> S&
        {
            line.str( other.line.str() );   // This can in theory throw, but.
            return *this;
        }

        S() = default;

        S( S const& other )
            : line( other.line.str() )
        {}
    };
}  // namespace my

auto main() -> int
{
    my::S o1, o2;
    o1 = o2;
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I tried with ostringstream, its the same error. I need any usable stream, just not `std::string` because i will need to create a local `ostringstream` and then copy it to that `string` – Łukasz Przeniosło Jul 01 '15 at 09:48
  • `ostringstream` doesnt have any method allowing you to check either it has any content or not and I need this in my application. – Łukasz Przeniosło Jul 01 '15 at 18:55
  • @ŁukaszPrzeniosło: You can very easily check whether an `ostringstream` has content or not, in various ways, and the code above shows half of one way to do it. I suggest that you ask a new question about this, because it's unrelated to the original question. There's generally good documentation at [cppreference.com](http://en.cppreference.com/w/cpp/io/basic_ostringstream). – Cheers and hth. - Alf Jul 01 '15 at 19:14
  • I see, thank you for answer. If the current aproach fails to i will try this asap. – Łukasz Przeniosło Jul 01 '15 at 19:34
3

Reason:

std::stringstream::operator= acquires the contents of its right hand side, by move-assigning its members and base classes.

In your overloaded operator= the input argument is const. Thus, the input argument's member logString cannot be moved. Additionally, the operator=(std::stringstream const&) in stringstream is declared deleted. Overload resolution chooses a deleted operator and rightfully you're getting a compile error.

Solution:

logline_t& operator =(const logline_t& a) {
  logString.str(a.logString.str());;
  logLevel = a.logLevel;
  currentTime = a.currentTime;
  return *this;
}

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168
  • I am getting error at this line all the time `struct logline_t` with `error: use of deleted function ‘std::basic_stringstream::basic_stringstream(const std::basic_stringstream&)’` it seems like it doesnt compile only because `stringstream` is a member. – Łukasz Przeniosło Jul 01 '15 at 10:08
  • @ŁukaszPrzeniosło have your tried the solution above? – 101010 Jul 01 '15 at 10:11
  • @ŁukaszPrzeniosło I posted a Live demo please take a look at it. It works fine. – 101010 Jul 01 '15 at 10:15
  • It doesnt compile for me, I am getting the same error all the time. – Łukasz Przeniosło Jul 01 '15 at 10:35
  • `error: use of deleted function ‘std::basic_stringstream::basic_stringstream(const std::basic_stringstream&)’` at struct definition – Łukasz Przeniosło Jul 01 '15 at 10:37
  • @ŁukaszPrzeniosło This can't be because in my solution there's no assignment of one stringstream to another. There must be something else going wrong. – 101010 Jul 01 '15 at 10:40
  • well at one point i am doing `logline_t retVal = move(*m_data.front());` where `deque m_data;` but this should work as it is kind of another scope. – Łukasz Przeniosło Jul 01 '15 at 10:41
1

The problem is you are trying to copy a std::stringstream object which is non copyable. You can get round this by not copying the object itself but copy its contents into the new std::stringstream.

Also you really need a copy constructor as well as the copy assignment operator. The need to provide both of these (and usually a destructor as well) is explained in the so-called Rule Of Three.

Also we need to add a default consructor because adding a copy constructor prevented the compiler from generating its own default constructor.

struct logline_t
{
    std::stringstream logString; /*!< String line to be saved to a file (and printed to cout). */
    ElogLevel logLevel; /*!< The \ref ElogLevel of this line. */
    timeval currentTime; /*!< time stamp of current log line */

    // default constructor needed because we made a copy constructor
    // which deleted the compiler generated default constructor!
    logline_t()
    : logLevel(0) // set suitable defaults
    {
        gettimeofday(&currentTime, 0); // for example
    }

    // copy constructor
    logline_t(const logline_t& ll)
    : logString(ll.logString.str()) // construct from contents .str()
    , logLevel(ll.logLevel)
    , currentTime(ll.currentTime)
    {
    }

    // copy assignment operator
    logline_t& operator=(const logline_t& a)
    {
        logString.str(a.logString.str()); // assign from contents
        logLevel = a.logLevel;
        currentTime = a.currentTime;

        return *this;
    }
};
Galik
  • 47,303
  • 4
  • 80
  • 117