Martin Perry
Martin Perry

Reputation: 9527

C++ - auto casting to std::string

I have this code

template <typename T>
class KeyValueProperty {
protected:
    T value = T();
    std::string key = "";

public:
    KeyValueProperty(const std::string & key) : key(key) { }

    T & operator = (const T &i) { return value = i; };    

    operator const T & (){ return value; };    
};  


struct T2 {
    KeyValueProperty<std::string> x {"x"};  
    KeyValueProperty<double> y {"y"};   
};

and in main

T2 tx;
tx.x = "hellow";    
tx.y = 10;

std::cout << static_cast<std::string>(tx.x) << ::std::endl;
std::cout << tx.y << ::std::endl;

This is working correctly. However, doing only this

std::cout << tx.x << ::std::endl;

will end up in

error C2679: binary '<<': no operator found which takes a right-hand operand of type 'Test::KeyValueProperty' (or there is no acceptable conversion)

Is it possible to have auto-conversion, or I must manually call casting?

Upvotes: 3

Views: 5432

Answers (2)

GManNickG
GManNickG

Reputation: 503913

The reason t.y works even without a custom operator<< is because there already exists an operator<<(std::ostream&, double), and the compiler can also see that it can make a double out of your class. It does so and we're happy.

However, there is no operator<<(std::ostream&, std::string). If there was, the same logic would apply and we'd still be happy. Instead, there is:

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);

That is, a generic insertion operator for any kind of basic_string.

Although there exist some template arguments that would make this as if it were operator<<(std::ostream&, std::string), the compiler isn't going to try and guess which possible template arguments would allow it to subsequently match a conversion of your class to the result. There are too many combinations, so this isn't permitted.

This is why you had to explicitly turn your object into a std::string (aka std::basic_string<char>) - this removes one layer from the problem and it can do regular old type deduction to make this work.

The right solution is to give your wrapper class an insertion operator, to sidestep this issue.

Upvotes: 2

Richard Hodges
Richard Hodges

Reputation: 69892

You must provide an appropriate overload of operator<<, for example:

template<class T>
std::ostream& operator<<(std::ostream& os, KeyValueProperty<T> const& kvp)
{
    return os << T(kvp);
}

Upvotes: 1

Related Questions