Неоднозначность не улавливается компилятором

Мне пришлось потратить некоторое время на поиск и исправление ошибки, которую мне удалось выделить в следующем коде:

#include <iostream>

struct A
{
    std::string S;
    A(const std::string s) { S = s; }
};

void f1(A a) { std::cout << "f1:a.S = " << a.S << "\n"; }
void f1(const std::string s) { std::cout << "f1:s = " << s << "\n"; }

void f2(A a) { std::cout << "f2:a.S = " << a.S << "\n"; }

int main()
{
    f1(A("test"));
    f1(std::string("test"));

    f2(A("test"));
    f2(std::string("test"));

    return 0;
}

Ошибка была вызвана пропущенной (мной и компилятором (?)) неоднозначностью, созданной f1-функцией: f2 ясно показывает, что и f1(A), и f1(std::string) применимы к A, но при компиляции неоднозначность не улавливается компилятором. , и при выполнении вывод:

f1:a.S = test
f1:s = test
f2:a.S = test
f2:a.S = test

Правильно ли это поведение? Проблема с компилятором? Или просто старый добрый PIBCAK?


person slashmais    schedule 25.11.2012    source источник
comment
@Mat: Да, проблема возникает, когда я вызываю f1(std::string), ожидая A и не получая его :(   -  person slashmais    schedule 25.11.2012


Ответы (1)


Поведение, которое вы описываете, ожидаемо: двусмысленности нет. Неоднозначность разрешения перегрузки возникает, когда две перегрузки одинаково хорошо совпадают и обе являются «наилучшей перегрузкой».

Когда вы вызываете f1 с аргументом типа A, первое f1 является точным совпадением; второй f1 вообще не совпадает. Таким образом, f1 явно выигрывает при разрешении перегрузки.

Когда вы вызываете f1 с аргументом типа std::string, первый f1 соответствует через конструктор преобразования A; второй f1 является точным совпадением. Второй f1 соответствует лучше: это точное совпадение и преобразование не требуется. Две перегрузки не совпадают одинаково хорошо, поэтому двусмысленности нет. Второй f1 выигрывает при разрешении перегрузки.

person James McNellis    schedule 25.11.2012
comment
это точное совпадение, и преобразование не требуется: это точное совпадение, да, но необходимо выполнить (стандартное) преобразование, в данном случае квалификационное преобразование. - person ipc; 25.11.2012
comment
@ipc: преобразование квалификации не требуется. Квалификаторы cv верхнего уровня не являются частью типа функции и поэтому не влияют на разрешение перегрузки. - person James McNellis; 25.11.2012
comment
@slashmais: разрешение перегрузки таит в себе тонкости. - person James McNellis; 25.11.2012
comment
Тот факт, что cv-квалификация аргументов функции не является частью ее подписи, заключается в том, что они не имеют отношения к вызывающим объектам. Аргументы — это просто локальные переменные, инициализируемые во время вызова и доступные только внутри тела. Таким образом, никогда не следует применять const-квалификацию к аргументам в объявлениях функций, только к определениям - так как это означает, что вы можете изменить, является ли аргумент const в последнем, не чувствуя себя обязанным обновлять первый для соответствия, заставляя ваших пользователей перекомпилировать. (Это не имеет значения с точки зрения ABI, но make et al. не принимают это во внимание) - person underscore_d; 07.08.2016