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?

2
  • 3
    What "error" do you get? Build errors? Crashes? Unexpected output? Please copy-paste (as text) it if possible. Commented Mar 24, 2017 at 8:41
  • 3
    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.) Commented Mar 24, 2017 at 8:42

1 Answer 1

16

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.

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

4 Comments

good answer, maybe add that an explicit conversion should still get the desired result cout << (std::string)d << endl; I think
@xander 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.
@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?
@xander 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.

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.