Search code examples
c++visual-studio-2010c++11std-functionnullptr

Why is the ternary operator not the same as an if-else here?


I'm using TR1's std::function to implement a simple callback mechanism. If I don't want to get called back, I register nullptr as the callback handler. This compiles and works fine:

void Foo::MessageHandlingEnabled( bool enable ){
    if( enable )
        m_Bar.RegisterMessageHandler( std::bind(&Foo::BarMessageHandler, this, std::placeholders::_1) );
    else
        m_Bar.RegisterMessageHandler( nullptr );
}

If I rewrite this using the ternary operator...

void Foo::MessageHandlingEnabled( bool enable ){
        m_Bar.RegisterMessageHandler( enable?
                                      std::bind(&Foo::BarMessageHandler, this, std::placeholders::_1) :
                                      nullptr );   

 }

... VC++'s compiler says:

error C2446: ':' : no conversion from 'nullptr' to 'std::tr1::_Bind<_Result_type,_Ret,_BindN>' 1> with 1>
[ 1> _Result_type=void, 1> _Ret=void, 1>
_BindN=std::tr1::_Bind2,Foo *,std::tr1::_Ph<1>> 1> ] 1> No constructor could take the source type, or constructor overload resolution was ambiguous

Is this a limitation of the compiler, or am I doing something stupid? I know I might not gain any benefit, in this particular case, from using the ternary operator, but I'm just curious.


Solution

  • Both branches of the ternary operator must return values of the same type, or the type of one value must be convertible to the other.

    5.16.3 ... if the second and third operand have different types, and either has (possibly cv-qualified) class type, an attempt is made to convert each of those operands to the type of the other... [details omitted] Using this process, it is determined whether the second operand can be converted to match the third operand, and whether the third operand can be converted to match the second operand. If both can be converted, or one can be converted but the conversion is ambiguous, the program is ill-formed. If exactly one conversion is possible, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this section.

    This is why the compiler error says ...no conversion from 'nullptr' to 'std::tr1::_Bind<_Result_type,_Ret,_BindN>' 1>...

    nullptr has the type of std::nullptr_t and std::function<> has a constructor that accepts std::nullptr_t. std::tr1::_Bind can't be converted to std::nullptr_t or the other way around in the context of the ternary operator.

    if/else on the other hand, doesn't return anything at all.