Условный оператор не может разрешать перегруженные указатели на функции-члены

У меня небольшая проблема с указателями на перегруженные функции-члены в C++. Следующий код компилируется нормально:

class Foo {
public:
    float X() const;
    void X(const float x);
    float Y() const;
    void Y(const float y);
};

void (Foo::*func)(const float) = &Foo::X;

Но это не компилируется (компилятор жалуется, что перегрузки неоднозначны):

void (Foo::*func)(const float) = (someCondition ? &Foo::X : &Foo::Y);

Предположительно, это связано с тем, что компилятор сортирует возвращаемое значение условного оператора отдельно от типа указателя функции? Я могу обойти это, но мне интересно узнать, как спецификация говорит, что все это должно работать, поскольку это кажется немного неинтуитивным, и есть ли способ обойти это, не возвращаясь к 5 строкам if-then-else .

Я использую MSVC++, если это имеет значение.

Спасибо!


person Peter    schedule 14.09.2009    source источник
comment
Вы должны указать, что не работает означает. Ошибка компилятора? Неожиданное поведение во время выполнения?   -  person Steve Fallows    schedule 15.09.2009
comment
Сделал, спасибо за предложение.   -  person Peter    schedule 15.09.2009


Ответы (3)


Из раздела 13.4/1 («Адрес перегруженной функции», [over.over]):

Использование имени перегруженной функции без аргументов разрешается в определенных контекстах для функции, указателя на функцию или указателя на функцию-член для конкретной функции из набора перегрузки. В таких контекстах считается, что имя шаблона функции именует набор перегруженных функций. Выбранной функцией является функция, тип которой соответствует целевому типу, требуемому в контексте. Цель может быть

  • инициализируемый объект или ссылка (8.5, 8.5.3),
  • левая часть присваивания (5.17),
  • параметр функции (5.2.2),
  • параметр пользовательского оператора (13.5),
  • возвращаемое значение функции, операторной функции или преобразования (6.6.3) или
  • явное преобразование типа (5.2.3, 5.2.9, 5.4).

Перед именем функции перегрузки может стоять оператор &. Имя перегруженной функции не должно использоваться без аргументов в контекстах, отличных от перечисленных. [Примечание: любой избыточный набор скобок вокруг имени перегруженной функции игнорируется (5.1). ]

Цель, которую вы надеялись выбрать из приведенного выше списка, была первой, инициализируемым объектом. Но на пути есть условный оператор, и условные операторы определяют свои типы по своим операндам, а не по целевому типу.

Поскольку явные преобразования типов включены в список целей, вы можете отдельно преобразовать каждое выражение указателя-члена в условное выражение. Я бы сначала сделал typedef:

typedef void (Foo::* float_func)(const float);
float_func func = (someCondition ? float_func(&Foo::X) : float_func(&Foo::Y));
person Rob Kennedy    schedule 14.09.2009

Пытаться:

    void (Foo::*func1)(const float) = &Foo::X;
    void (Foo::*func2)(const float) = &Foo::Y;

    void (Foo::*func3)(const float) = (someCondition ? func1:func2);

Проблема в том, что тип результата оператора trinary определяется его аргументами.
В этой ситуации он не может определить тип результата, потому что типы ввода имеют несколько параметров. Только после того, как тип тройного оператора будет определен, он попытается выполнить присвоение.

person Martin York    schedule 14.09.2009

Пример:

class Foo {
public:
    void X(float x) {}
    void Y(float y)  {}
    float X() const;
};
typedef void (Foo::*Fff)(float);
Fff func = &Foo::X;
Fff func2 = true ? (Fff)&Foo::X : (Fff)&Foo::Y;

int main(){
    return 0;
}

Вам нужно немедленно вызвать &Foo::X, чтобы устранить перегрузку. Обратите внимание, что если вы закомментируете перегруженный float X(), вам не нужно этого делать.

Похоже, что компилятор недостаточно умен, чтобы вывести требуемый тип возвращаемого значения троичного выражения (это может быть ошибкой).

person Jonathan Graehl    schedule 14.09.2009
comment
someCondition не является константой времени компиляции, хотя это, вероятно, не совсем очевидно из приведенного мной упрощенного примера. Я мог бы превратить его в один с помощью шаблона, но я думаю, что в целом это было бы уродливее... :) - person Peter; 15.09.2009
comment
В любом случае, это не обязательно должно быть одно. - person Jonathan Graehl; 15.09.2009