Я использую TR1 std::function
реализовать простой механизм обратного вызова. Если я не хочу, чтобы мне перезвонили, я регистрируюсь nullptr
в качестве обработчика обратного вызова. Это компилируется и работает нормально:
void Foo::MessageHandlingEnabled( bool enable ){
if( enable )
m_Bar.RegisterMessageHandler( std::bind(&Foo::BarMessageHandler, this, std::placeholders::_1) );
else
m_Bar.RegisterMessageHandler( nullptr );
}
Если я перепишу это, используя троичный оператор …
void Foo::MessageHandlingEnabled( bool enable ){
m_Bar.RegisterMessageHandler( enable?
std::bind(&Foo::BarMessageHandler, this, std::placeholders::_1) :
nullptr );
}
… Компилятор VC ++ говорит:
ошибка C2446: ‘:’: нет преобразования из ‘nullptr’ в
«Станд :: tr1 :: _ Bind<_Result_type, _Ret, _BindN> ‘1> с 1>
[1> _Result_type = void, 1> _Ret = void, 1>
_BindN = станд :: tr1 :: _ Bind2, Foo
*, Станд :: tr1 :: _ Ph<1 >> 1>] 1> Ни один конструктор не может принять тип источника, или разрешение перегрузки конструктора было неоднозначным
Это ограничение компилятора, или я делаю что-то глупое? Я знаю, что в этом конкретном случае, возможно, не получу никакой выгоды от использования троичного оператора, но мне просто любопытно.
Обе ветви троичного оператора должны возвращать значения одного и того же типа, или тип одного значения должен быть конвертируемым в другое.
5.16.3 … если второй и третий операнды имеют разные типы и имеют либо (возможно, cv-квалифицированный) тип класса, делается попытка преобразовать каждый из этих операндов в тип другого … [подробности опущены ] Используя этот процесс, определяется, может ли второй операнд быть преобразован, чтобы соответствовать третьему операнду, и может ли третий операнд быть преобразован, чтобы соответствовать второму операнду. Если оба могут быть преобразованы, или один может быть преобразован, но преобразование неоднозначно, программа плохо сформирована. Если возможно только одно преобразование, это преобразование применяется к выбранному операнду, а преобразованный операнд используется вместо исходного операнда в оставшейся части этого раздела.
Вот почему ошибка компилятора говорит ...no conversion from 'nullptr' to 'std::tr1::_Bind<_Result_type,_Ret,_BindN>' 1>...
nullptr
имеет тип std::nullptr_t
а также std::function<>
имеет конструктор, который принимает std::nullptr_t
, std::tr1::_Bind
не может быть преобразован в std::nullptr_t
или наоборот в контексте троичного оператора.
if/else
с другой стороны, ничего не возвращает вообще.
Здесь я полагаю, что registerHandler имеет полиморфное объявление.
Я предполагаю, что при встрече с тернарным оператором компилятор примет обе части: одного типа.
Таким образом, разрешение полиморфизма registerHandler совпадает с тем, который принимает аргумент, совместимый с результатом связывания.
С помощью if каждый вызов registerHandler разрешается отдельно, поэтому выбор правильного правильного registerHandler зависит от каждого переданного типа параметра.
Максим Егорушкин ответ правильный. Вот простой обходной путь с примером кода, который лучше иллюстрирует вашу проблему:
struct Base{};
struct DerivedA:public Base{};
struct DerivedB:public Base{};
DerivedA a;
DerivedB b;
doesNotWork()
{
bool chooseA = true;
Base& base = chooseA?a:b; // Error: compiler tries to convert b to DerivedA (the type of a).
}
Base& choose(bool x)
{
if(x) return a;
return b;
}
works()
{
bool chooseA = true;
Base& base = choose(chooseA); //Helper function converts a or b to parent class Base.
}