Функция с типом параметра, у которого есть конструктор копирования с выбранным неконфектом?

StackOverflow https://stackoverflow.com/questions/4704567

Вопрос

Некоторое время назад я был смущен следующим поведением какого -то кода, когда хотел написать 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. - конечный пример ] Когда параметр имеет тип класса, а выражение аргумента имеет одинаковый тип, неявная последовательность преобразования - это преобразование идентификации. [...]

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top