简体   繁体   中英

How to use enable_if to conditionally define a << operator

I'm trying to define a << operator for a set of classes; the set is open, but all of the members have a common tagging base class, and all have the member function std::string String() const . Basically, what I've got is:

class Tag {};
class Obj : public Tag
{
public:
    std::string String() const { return "specialized"; }
};

template <typename T>
typename std::enable_if<std::is_base_of<Tag, T>::type, std::ostream>::value& operator<<( std::ostream& dest, T const& source)
{
    dest << source.String();
    return dest;
}

int
main()
{
    std::cout << typeid(std::enable_if<std::is_base_of<Tag, Obj>::value, std::ostream>::type).name() << std::endl;
    std::string s( "generic" );
    Obj e;
    std::cout << e << std::endl;
    std::cout << s << std::endl;
    return 0;
}

This doesn't work: with g++ (version 4.8.3, invoked with -std=c++11 ), I get the error message:

enableIf.cc: In function 'int main()':
enableIf.cc:55:18: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
     std::cout << e << std::endl;
                  ^
In file included from /usr/lib/gcc/x86_64-pc-cygwin/4.8.3/include/c++/iostream:39:0,
                from enableIf.cc:8:
/usr/lib/gcc/x86_64-pc-cygwin/4.8.3/include/c++/ostream:602:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = Obj]'
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
     ^

I can't figure it out, because there aren't any rvalue-references in sight; the compiler seems to have struck on the generic overload for std::ostream&& in the standard library.

With MSC (VS 2013), the error message is a lot more verbose, but it starts with:

enableIf.cc(55) : error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Obj' (or there is no acceptable conversion)

and then goes on to list a lot of possible functions, all in the standard library.

(In my actual code, line 55 corresponds to the line std::cout << e << std::endl; .)

In both cases, the compiler seems to be rejecting my overloaded function. If I comment out the << lines, however, the code compiles, and the value output by the first line in main seems correct (at least with MSC—the output of g++ is So , what ever that's supposed to mean).

Given that two compilers agree, I assume that there is an error in my code, but I can't figure out what. How do you do this? (FWIW: I'd be equally happy, or even happier, with a solution which generates the overload for all types having a member function std::string Type::String() const .)

I'm pretty sure you meant this:

template <typename T>                        //  here                 here
typename std::enable_if<std::is_base_of<Tag, T>::type, std::ostream>::value& 
operator<<( std::ostream& dest, T const& source)

to be this:

template <typename T>
typename std::enable_if<std::is_base_of<Tag, T>::value, std::ostream>::type& 
operator<<( std::ostream& dest, T const& source)

after changing as such, you compile successfully.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM