Функция с типом параметра, у которого есть конструктор копирования с выбранным неконфектом?
-
11-10-2019 - |
Вопрос
Некоторое время назад я был смущен следующим поведением какого -то кода, когда хотел написать is_callable<F, Args...>
черта. Разрешение на перегрузку не вызывает функций, принимающих аргументы неконтролируемым рефери, верно? Почему он не отвергается в следующем, потому что конструктор хочет Test&
? Я ожидал, что это возьмет f(int)
!
struct Test {
Test() { }
// I want Test not be copyable from rvalues!
Test(Test&) { }
// But it's convertible to int
operator int() { return 0; }
};
void f(int) { }
void f(Test) { }
struct WorksFine { };
struct Slurper { Slurper(WorksFine&) { } };
struct Eater { Eater(WorksFine) { } };
void g(Slurper) { }
void g(Eater) { } // chooses this, as expected
int main() {
// Error, why?
f(Test());
// But this works, why?
g(WorksFine());
}
Сообщение об ошибке
m.cpp: In function 'int main()':
m.cpp:33:11: error: no matching function for call to 'Test::Test(Test)'
m.cpp:5:3: note: candidates are: Test::Test(Test&)
m.cpp:2:3: note: Test::Test()
m.cpp:33:11: error: initializing argument 1 of 'void f(Test)'
Не могли бы вы объяснить, почему один работает, а другой - нет?
Решение
Разрешение на перегрузку выбирает функцию, которая является наиболее близким соответствием с предоставленным аргументом. Вы предоставили тест. Нет необходимости преобразования - преобразование идентичности используется. Таким образом, разрешение функции выбирает F (тест). Тест не может быть скопирован из RValue, который вы предоставили, но разрешение перегрузки уже преуспела ... преобразование в INT никогда не проверяется.
g(Eater)
Выбирается потому, что типы точно не совпадают, преобразование идентификации не используется, а компилятор должен найти процедуру преобразования, которая работает. g(Slurper)
Не потому, что вы не можете сделать один из предоставленных аргументов.
"Почему это не терпит неудачу: struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }
"
Потому что F (A &) не является жизнеспособной перегрузкой для предоставленного аргумента. В этом случае параметр является ссылкой, и тот факт, что TEMP не связываются с неконфектом, разрешено осуществлять разрешение. В этом случае это происходит, и эта версия функции становится не кандидатом, оставляя только одну, и она работает.
Другие советы
По сути, для разрешения перегрузки предполагается, что объект типа А может быть преобразован в объект типа А, независимо от какой-либо квалификации CV на любом из двух.
Из проекта N1905:
13.3.3.1: перегрузка. Разгрузка разрешения.
6 Когда тип параметра не является ссылкой, неявная последовательность преобразования моделирует копию инициализацию параметра из выражения аргумента. Неявная последовательность преобразования - это та, которая должна преобразовать выражение аргумента в RVALUE типа параметра. [ Примечание: Когда параметр имеет тип класса, это концептуальное преобразование, определяемое для целей пункта 13; Фактическая инициализация определяется с точки зрения конструкторов и не является конверсией. - Конечная примечание ] Любая разница в квалификации CV верхнего уровня подчиняется самой инициализации и не составляет преобразования. [ Пример: Параметр типа A может быть инициализирован из аргумента типа Const A. Неявная последовательность преобразования для этого случая - последовательность идентификации; Он не содержит «преобразования» от const A в A. - конечный пример ] Когда параметр имеет тип класса, а выражение аргумента имеет одинаковый тип, неявная последовательность преобразования - это преобразование идентификации. [...]