Why cannot use cout with user-defined conversion to std::string?
Asked Answered
R

1

9

Here I define a Date, and specify a user-defined conversion.

class Date {
private:
    int day;
    int month;
    string dateStr;
public:
    Date(int _day, int _month) : day(_day), month(_month) {}

    operator const string() {
        ostringstream formattedDate;
        formattedDate << month << "/" << day;
        dateStr = formattedDate.str();
        return dateStr;
    }
};

It works well when converting to string.

Date d(1, 1);
string s = d;

But why cannot use it with cout directly?

cout << d << endl; // The compiler complains that there is no suitable type marching << operator

However, if I use char* instead of string for user-defined conversion, I can use it with cout directly. Why?

operator const char*() { 
    ostringstream formattedDate;
    formattedDate << month << " / " << day;
    dateStr = formattedDate.str();
    return dateStr.c_str();
}

ps. I know that overloading << directly will work well for output. But my question is: why cannot use << with user-defined conversion to std::string?

Radix answered 24/3, 2017 at 8:39 Comment(2)
What "error" do you get? Build errors? Crashes? Unexpected output? Please copy-paste (as text) it if possible.Visceral
Also, if you want output of your objects, I rather recommend you overload the global operator<< function instead of relying on conversion operators. (And if you still want to have a conversion operator, you can use the overloaded << operator to create it.)Visceral
M
15

Note that std::string is an instantiation from template std::basic_string, the signature of operator<< for std::string is indeed:

template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>& 
    operator<<(std::basic_ostream<CharT, Traits>& os, 
               const std::basic_string<CharT, Traits, Allocator>& str);

then for cout << d;, the template parameter of std::basic_string has to be deduced; but type deduction does not consider implicit conversions;

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

That means the deduction would fail; then no candidate function is viable.

But operator<< for const char* hasn't such kind of problem; implicit conversion takes effect and then works fine.

To solve the issue, you can use explicit conversion to avoid template argument deduction; or overload operator<< on the type Date directly.

Mammy answered 24/3, 2017 at 8:51 Comment(4)
good answer, maybe add that an explicit conversion should still get the desired result cout << (std::string)d << endl; I thinkBlockbuster
@Blockbuster Please don't suggest using C-style casts in C++. They're the most dangerous of all the possible ways to do a type conversion.Vorticella
@Angew but that is not a classic cast, it's an explicit type conversion calling the operator string(). I'm not sure how to call it with C++ cast style, can you use a static_cast<std::string>(d) for this or what would be the proper way?Blockbuster
@Blockbuster Yes, you'd use static_cast, or you could also use std::string{d}. (Type)expr is always a C-style cast and can do any of the following: static, static+const, reinterpret, reinterpret+const; depending on Type and expr. That's why it's such a bad idea to use it: you don't really know what will happen.Vorticella

© 2022 - 2024 — McMap. All rights reserved.