type - operator() c++
Компилятор считает, что «A(A &)» принимает значения на мгновение? (1)
У меня есть этот код
struct A { A(); A(A&); };
struct B { B(const A&); };
void f(A);
void f(B);
int main() {
f(A());
}
К моему удивлению, это не сработало с GCC и Clang. Кланг говорит, например,
Compilation finished with errors:
source.cpp:8:10: error: no matching constructor for initialization of 'A'
f(A());
^~~
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument
struct A { A(); A(A&); };
^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
struct A { A(); A(A&); };
^
source.cpp:4:13: note: passing argument to parameter here
void f(A);
Почему они выбирают первый f
, когда второй f
работает нормально? Если я уберу первый f
, то вызов будет успешным. Что более странно для меня, если я использую инициализацию скобки, он также работает нормально
int main() {
f({A()});
}
Все они называют второй f
.
https://code.i-harness.com
Это языковая причуда. Первый f
соответствует лучше, потому что ваш A
требует преобразования для соответствия типу аргумента ( A
), но когда компилятор пытается выполнить вызов, тот факт, что не найден подходящий конструктор копирования, приводит к сбою вызова. Этот язык не позволяет учитывать осуществимость фактического вызова при выполнении шага разрешения перегрузки.
Наиболее близкое соответствие стандартной цитате ISO / IEC 14882: 2011 13.3.3.1.2 Пользовательские последовательности преобразования [over.ics.user]:
Преобразованию выражения типа класса в тот же тип класса присваивается рейтинг точного соответствия, а преобразованию выражения типа класса в базовый класс этого типа присваивается рейтинг преобразования, несмотря на то, что копирование / перемещение конструктор (т. е. определяемая пользователем функция преобразования) вызывается для этих случаев.
В случае инициализации списка вам, вероятно, нужно взглянуть на: 13.3.3.1.2 Пользовательские последовательности преобразования [over.ics.user]
Когда объекты неагрегированного класса типа T инициализируются списком (8.5.4), разрешение перегрузки выбирает конструктор в два этапа:
- Первоначально функции-кандидаты являются конструкторами списка инициализаторов (8.5.4) класса T, а список аргументов состоит из списка инициализаторов как единственного аргумента.
- Если жизнеспособный конструктор списка инициализаторов не найден, разрешение перегрузки выполняется снова, где все функции-кандидаты являются конструкторами класса T, а список аргументов состоит из элементов списка инициализатора.
Поскольку разрешение перегрузки должно искать жизнеспособные конструкторы в каждом случае для f(A)
и f(B)
оно должно отклонять последовательность попыток связать A()
с A(A&)
но B(const A&)
все еще жизнеспособен.