Условный оператор не может разрешить перегруженные указатели на функции-члены - PullRequest
5 голосов
/ 15 сентября 2009

У меня небольшая проблема, связанная с указателями на перегруженные функции-члены в 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 ++, если это что-то меняет.

Спасибо!

Ответы [ 3 ]

7 голосов
/ 15 сентября 2009

Из раздела 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));
1 голос
/ 15 сентября 2009

Попробуйте:

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

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

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

1 голос
/ 15 сентября 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 (), вам это не нужно.

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

...