Search code examples
c++boostoperator-overloadingboost-spirit

Implicit cast doesn't work for BOOST_STRONG_TYPEDEF and BOOST_SPIRIT_DEBUG_NODE


I have defined a boost::spirit::qi rule:

boost::spirit::qi::rule<Iterator, Identifier()> id;

where Identifier is defined by:

BOOST_STRONG_TYPEDEF(std::string, Identifier)

but when I use

BOOST_SPIRIT_DEBUG_NODE(id);

It fails to compile with following error:

boost_1_51_0/boost/spirit/home/support/attributes.hpp:1203: error: no match for 'operator<<' in 'out << val'

and it lists the overloaded operators of ostream.

Knowing that BOOST_STRONG_TYPEDEF defines a cast operator to the original type, shouldn't compiler implicitly cast from Identifier to std::string when using operator<<? or is there a restriction that prevents compiler to apply a cast operator of a type when it is trying to match the other operator (namely operator<< )?

When I define the following operator it compiles:

inline std::ostream& operator<<(std::ostream& os, const Identifier& id)
{
    return os << static_cast<std::string const&>(id);
}

I am using gcc4.4.2


Solution

  • This has nothing to do with boost, strong_typedef or spirit.

    It has a lot to do with type deduction for template arguments. In short, when argument types are deduced, implicit conversions never take place [1]

    Cf.:

    #include <iostream>
    #include <string>
    #include <boost/strong_typedef.hpp>
    
    BOOST_STRONG_TYPEDEF(double, X)
    int main() { std::cout << X(); }
    

    No problem! Replace double by std::string, and it doesn't work anymore. What's different?

    The declaration of the streaming operator differs.

    Contrast

    ostream& ostream::operator<<(double);
    

    To

    template<typename _CharT, typename _Traits, typename _Alloc>
       inline basic_ostream<_CharT, _Traits>&
       operator<<(basic_ostream<_CharT, _Traits>&, basic_string<_CharT, _Traits, _Alloc> const&)
    

    The fact that the operator overload is a function template disallows any implicit conversions.


    [1] I guess initializer_list may look like a bit of an exception here, what with widening/narrowing that it can do. Different subject, though