0

I wrote the following utility C++ class.

/*!
 * \brief The \b StringBuilder class is used for efficiently constructing long
 * strings out of many small ones. Internally it uses \b std::ostringstream and
 * its advantage compared to usage of raw \b std::ostringstream is that it is
 * capable to be used on single line and implicitly converted to \b std::string
 * everywhere this type is expected.
 * \code{.cpp}
 * void foo(const std::string& s);
 * foo(utils::StringBuilder("The answer is: ") << 42 << std::endl);
 * \endcode
 */
class StringBuilder
{
public:
    StringBuilder() = default;

    template <class... Args>
    explicit StringBuilder(Args&&... args)
    {
        append(std::forward<Args>(args)...);
    }

    template <class T>
    StringBuilder& append(const T& arg)
    {
        _data << arg;
        return *this;
    }

    template <class T, class... Args>
    StringBuilder& append(const T& arg, Args&&... args)
    {
        _data << arg;
        append(std::forward<Args>(args)...);
        return *this;
    }

    std::string toString() const
    {
        return _data.str();
    }

    operator std::string() const
    {
        return toString();
    }

    template <class T>
    StringBuilder& operator<<(const T& object)
    {
         return append(object);
    }

private:
    std::ostringstream _data;
};

cannot compile. The error message is quite long to paste here, but it start with:

main.cpp: In function ‘int main()’: main.cpp:37:8: error: no match for ‘operator<<’ (operand types are ‘utils::StringBuilder’ and ‘’) sb << endl;

and ends with:

/usr/include/c++/4.8.3/bits/basic_string.h:2753:5: note: template argument deduction/substitution failed: main.cpp:36:33: note:
‘utils::StringBuilder’ is not derived from ‘std::basic_ostream<_CharT, _Traits>’ cout << (StringBuilder() << endl);

How to make StringBuilder to be able to accept std::endl and other IO manipulators?

2
  • 2
    Doesn't std::ostringstream already do this? What's your actual need to roll your own? Commented Jan 16, 2015 at 15:53
  • I'm described the need in the comment above. Commented Jan 16, 2015 at 16:24

1 Answer 1

2

endl (as well as the iomanip operands) is a function.

Just write an overload like this

StringBuilder& operator<<(ostream& func(ostream&))
{
    _data << func;
    return *this;
}

.. or as an external operator like this

StringBuilder& operator<<(StringBuilder& sb, ostream& func(ostream&)) ...

...and it will work for all of them

Sign up to request clarification or add additional context in comments.

3 Comments

Can you explain why this works with in class operator<< but do not works with friend function and why in the both cases do not work with std::ostringstream instead of std::ostream?
Because endl is a function with ostream and not ostringstream argument/return value. I'm not sure why it doesn't work as an external operator for you. It does for me. Just write it as a member, like I did
But std::ostringstream is derived from std::ostream. Why there is not implicit downcast?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.