开发者

Overloading the global type conversion operator

开发者 https://www.devze.com 2022-12-19 14:03 出处:网络
To test and display the result of some functions of my library, I am creating a set of handy functions.

To test and display the result of some functions of my library, I am creating a set of handy functions.

I have an execute function that looks like :

template <typename R, typename I>
std::string execute( const std::string& func_name, R(*func_ptr)( const I& ), const I& func_input );

It calls the function, and display the results and arguments in a formatted string that I can send to std::cout.

The problem is that some of my functions do not return convertible-to-string results. I thought I could simply overload the global ::operator std::string with something like:

template <typename T>
operator std::string( const std::vector<T>& v );

But GCC complains:

error: 'operator std::string(const std::vector<T, std::allocator<_CharT> >&)' must be a nonstatic member function

Well, the problem of course is that I cannot add member operators to std::vector, and even for my classes, I don't want to pollute them with "for testing" conversion operators.

I guess that I can add a l开发者_运维技巧ayer of indirection and use a function instead of a conversion operator, but that would not be the more aesthetic solution. I could also overload ::operator << for std::ostream and use a std::ostringstream, but that also is not the cleanest solution.

I wondered if the global conversion operator is really not overloadable, and if so, why.


Conversion operators (cast operators) must be a member of the convertible class that produces the converted type. As assignment operators, they must be member functions, as your compiler is telling you.

Depending on how much effort you want to put into the debug part of it, you could try to use metaprogramming to forward your execute method to different actual implementations, providing specific ones for containers that will print the contents.

Why don´t you want to provide operator<< for your types? I think that is actually the idiomatic solution. Unlike other languages that you use methods that convert to string to produce printable results, in C++ the idiomatic way is providing operator<< and then using stringstreams (or boost::lexical_cast or some similar solution) to convert to strings based on the operator<< implementation. There is a simple utility class here to create a string from elements that override operator<< if you want to use that for a start point.


I wondered if the global conversion operator is really not overloadable, and if so, why.

No, there is no such thing. Conversion functions must be a member of a class. If it weren't so, it would make overload resolution a particularly vexing problem for the compiler by introducing ambiguities.


There is no user defined global conversion operator. You must control either the target type (in which case a non explicit one parameter constructor is the conversion operator) or the source type (in which case you have to overload the member operator target()).


A conversion function must be a member function. The function may not specify a return type, and the parameter list must be empty. They should be used sparingly and there should be a clear conversion path from one type to another type. Otherwise they can lead to unexpected results and mysterious errors.


Unfortunately there is no such thing as a global casting operator. Surprisingly. But templates are your friend.

Sometimes you do not want to expose the casting to the interface put would want to keep this anonymous for a specific implementation only. I usually add a template as() method to the class which can also do type checks in the cast, etc. and lets you handle the way you want to implement the casting (e.g. dynamic, shared, ref, etc).

Something like this:

    template< class EvtT >const EvtT& as() const throw( std::exception )
    {
        const EvtT* userData = static_cast<const EvtT*>( m_UserData );
        ASSERT( userData, "Fatal error! No platform specific user data in input event!" );
        return *userData;
    }

m_UserData is an anonymous type which only the implementation knows. While this is strictly a non-typed cast (I do not use type cecks here) this could be replaced by a dynamic_cast and proper casting exceptions.

The implementation simply does this:

    unsigned char GetRawKey( const InputEvent& ie )
    {
        const UserEvent& ue = ie.as<const UserEvent>();
        ...
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号