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&) все еще жизнеспособен.





brace-initialization