Подтвердить что ты не робот

Разрешение перегрузки и массивы: какую функцию следует вызывать?

Рассмотрим следующую программу:

#include <cstddef>
#include <cstdio>

void f(char const*&&)      { std::puts("char const*&&");      } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)

template <std::size_t N>
void f(char const (&)[N])  { std::puts("char const(&)[N]");   } // (3)

int main()
{
    const char data[] = "a";
    f(data);
}

Какой f должен быть вызван? Почему?

Последние выпущенные версии трех компиляторов не согласны с ответом на этот вопрос:

  • (1) вызывается при компиляции программы с помощью g++ 4.5.2
  • (2) вызывается, когда программа скомпилирована с использованием Visual С++ 2010 SP1
  • (3) вызывается при компиляции программы с помощью Clang 3.0 (соединительная линия 127530)

Знают ли правила разрешения перегрузки существенно в разных черновиках С++ 0x? Или, два из этих компиляторов действительно просто полностью ошибаются? Какая перегрузка - это правильная перегрузка, которая должна быть выбрана за последний проект С++ 0x?

4b9b3361

Ответ 1

Во-первых, последовательность преобразования всех трех одинакова, за исключением того, что для первых двух существует преобразование lvalue (преобразование lvalue в rvalue), которое, однако, не используется для упорядочения последовательностей преобразования. Все три являются точными совпадениями (спецификация шаблона функции имеет тип параметра char const(&)[2]).

Если вы перебираете правила в 13.3.3.2p3, вы останавливаетесь в этом абзаце

S1 и S2 являются привязками привязки (8.5.3), и ни один из них не ссылается на неявный параметр объекта нестатической функции-члена, объявленной без реф-определителя, а S1 связывает ссылку rvalue с rvalue, а S2 связывает значение lvalue ссылка.

Последовательность преобразования не может быть сформирована, если требуется привязать ссылку rvalue к lvalue, спецификация говорит в 13.3.3.1.4p3. Если вы посмотрите, как привязка привязки работает на последней пуле 8.5.3p5, она создаст временное (я думаю, что они означают временное значение rvalue) типа char const* из массива lvalue и привяжет ссылку к этому временному. Поэтому, думаю, (1) лучше, чем (2). То же самое относится к (1) к (3), хотя нам это не понадобится, потому что (3) является шаблоном, поэтому в галстуке мы снова выбираем (1).

В n3225 они изменили правила привязки ссылок, так что ссылки rvalue могут связываться с выражениями инициализатора, которые являются lvalues, до тех пор, пока ссылка будет привязана к rvalue (возможно, созданной путем правильного преобразования инициализатора раньше). Это может повлиять на обработку Visual С++, которая может быть не актуальна здесь.

Я не уверен насчет clang. Даже если он будет игнорировать (1), тогда он окажется в пределах между (2) и (3), и ему нужно будет выбрать (2), потому что это не шаблон.


Я думаю, что последняя пуля 8.5.3p5 запуталась, потому что она говорит "В противном случае временный тип..". Неясно, относится ли временное значение как lvalue или как rvalue к 13.3.3.1.4p3, что означает, что я не уверен, как должно вести себя следующее в соответствии с точными словами spec

void f(int &);
void f(int &&);

int main() {
  int n = 0;
  f(n);
}

Если мы предположим, что временное отношение рассматривается как rvalue по предложению 13, тогда мы привязываем rvalue ref к rvalue во второй функции и lvalue в первом. Поэтому мы выберем вторую функцию, а затем получим диагностику по последней пуле 8.5.3p5, потому что T1 и T2 связаны с привязкой. Если мы предположим, что временное отношение рассматривается как lvalue в соответствии с пунктом 13, то следующее не будет работать

void f(int &&);
int main() {
  f(0);
}

Потому что мы привязываем rvalue ref к lvalue, который в соответствии с пунктом 13 сделает функцию нежизнеспособной. И если мы интерпретируем "связывание rvalue ref с lvalue", чтобы ссылаться на выражение инициализатора вместо связанного с ним окончательного выражения, мы не будем принимать следующие

void f(float &&);
int main() {
  int n = 0;
  f(n);
}

Это, однако, действует с n3225. Так что, похоже, какая-то путаница - я отправил DR в комитет об этом.

Ответ 2

Я утверждаю, что # 3 - это функция, выбранная соответствующим компилятором.

(1) лучше, чем (2), потому что "Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если S1 и S2 являются привязками привязки (8.5.3), и ни один из них не ссылается на неявный параметр объекта нестатическая функция-член, объявленная без ref-quali fi, и S1 связывает ссылку rvalue с rvalue, а S2 связывает ссылку lvalue."

(3) лучше, чем оба (1) и (2), потому что это преобразование идентичности (другие - точные преобразования соответствия) и "Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если S1 правильная подпоследовательность S2 (сравнение конверсионных последовательностей в канонической форме, определенных в 13.3.3.1.1, исключая любую трансформацию Lvalue, последовательность преобразования идентичности считается подпоследовательностью любой последовательности, не связанной с идентичностью)"

Шаблон против не-шаблона рассматривается только тогда, когда ни одно преобразование не лучше "или, если не тот..."

достаточно странно, хотя Комо предпочитает (2) более (3). Этот тестовый пример не скомпилируется:

#include <cstddef>
#include <cstdio>

// (1) removed because Comeau doesn't support rvalue-references yet
char f(char const* const&) { std::puts("char const* const&"); return 0; } // (2)

template <std::size_t N>
int f(char const (&)[N])  { std::puts("char const(&)[N]"); return 0; } // (3)

int main()
{
    const char data[] = "a";
    switch (0) {
       case sizeof(char):
           break;
       case sizeof(f(data)):
           break;
    }
}

Ответ 3

Это ответ wiki сообщества для сбора отрывков из стандарта (черновик 3225).

раздел 13.3.3 "Наилучшая жизнеспособная функция" [over.match.best]

  • Определите ICSi (F) следующим образом:

    • если F является статической функцией-членом, ICS1 (F) определяется так, что ICS1 (F) не лучше и не хуже ICS1 (G) для любой функции G и, симметрично, ICS1 (G) не лучше и не хуже, чем ICS1 (F); в противном случае,

    • пусть ICSi (F) обозначает неявную последовательность преобразований, которая преобразует i-й аргумент в список в тип i-го параметра жизнеспособной функции F. 13.3.3.1 определ ет неявные последовательности преобразовани и 13.3.3.2 определяет, что означает, что одна неявная последовательность преобразований является лучшей последовательностью конверсии или худшей последовательностью преобразования, чем другая.

    Учитывая эти определения, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов я ICSi (F1) не является худшей последовательностью преобразования, чем ICSi (F2), и затем

    • для некоторого аргумента j, ICSj (F1) является лучшей последовательностью преобразования, чем ICSj (F2)

    или, если это не так,

    • контекст представляет собой инициализацию путем пользовательского преобразования (см. 8.5, 13.3.1.5 и 13.3.1.6) и стандартную последовательность преобразования из возвращаемого типа F1 в тип назначения (т.е. тип объект инициализируется) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из возвращаемого типа F2 в тип адресата

    или, если это не так,

    • F1 - это функция без шаблона, а F2 - специализированная функция шаблона

    или, если это не так,

    • F1 и F2 - специализированные шаблоны шаблонов функций, а шаблон функции для F1 - более специализированный чем шаблон для F2 в соответствии с правилами частичного упорядочения, описанными в 14.5.6.2.
  • Если существует только одна жизнеспособная функция, которая является лучшей функцией, чем все другие жизнеспособные функции, то она выбирается с помощью разрешения перегрузки; в противном случае вызов плохо сформирован.

раздел 13.3.3.1.4 "Связывание ссылок" [over.ics.ref]

  • Когда параметр ссылочного типа напрямую связывается (8.5.3) с выражением аргумента, неявное преобразование последовательность - это преобразование идентичности, если выражение аргумента не имеет тип, который является производным классом тип параметра, и в этом случае неявная последовательность преобразования представляет собой преобразование с производной базой (13.3.3.1). Если параметр привязывается непосредственно к результату применения функции преобразования к аргумент-выражение, неявная последовательность преобразования представляет собой последовательность, определяемую пользователем (13.3.3.1.2), со второй стандартной последовательностью преобразования либо преобразование идентичности, либо, если функция преобразования возвращает объект типа, который является производным классом типа параметра, преобразование с производной базой.

  • Если параметр ссылочного типа не связан непосредственно с выражением аргумента, последовательность преобразования это тот, который требуется для преобразования выражения аргумента в базовый тип ссылки в соответствии с до 13.3.3.1. Концептуально эта последовательность преобразования соответствует инициализации копирования временного базовый тип с выражением аргумента. Любая дифференциация в верхнем уровне cv-qualifation подразделяется на сама инициализация и не представляет собой преобразования.

раздел 13.3.3.2 "Ранжирование неявных последовательностей преобразования" [over.ics.rank]

  • 13.3.3.2 определяет частичное упорядочение неявных последовательностей преобразования на основе улучшений отношения последовательности и лучшего преобразования. Если неявная последовательность преобразования S1 определяется этими правилами, чтобы быть лучше чем S2, то также имеет место, что S2 является худшей последовательностью преобразования, чем S1. Если последовательность преобразования S1 не лучше и хуже, чем последовательность преобразования S2, S1 и S2 быть неразличимыми последовательностями преобразования.

  • При сравнении основных форм неявных последовательностей преобразования (как определено в 13.3.3.1)

    • стандартная последовательность преобразования (13.3.3.1.1) является лучшей последовательностью преобразования, чем пользовательская последовательность преобразования или последовательность преобразования многоточия, и

    • пользовательская последовательность преобразования (13.3.3.1.2) является лучшей последовательностью преобразования, чем последовательность преобразования многоточия (13.3.3.1.3).

  • Две неявные последовательности преобразования одной и той же формы являются неотличимыми последовательностями преобразования, если только один из применяются следующие правила:

    • Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

      • S1 является собственной подпоследовательностью S2 (сравнивая последовательности преобразования в канонической форме, определенные в 13.3.3.1.1, исключая любую преобразование Lvalue, последовательность преобразования идентичности рассматривается как подпоследовательность любой последовательности, не являющейся идентификационной конверсией)

      или, если это не так,

      • ранг S1 лучше ранга S2, или S1 и S2 имеют одинаковый ранг и различимы по правилам в параграфе ниже

      или, если это не так,

      • S1 и S2 отличаются только их квалификационным преобразованием и дают аналогичные типы T1 и T2 (4.4) соответственно, а cv-квалификационная сигнатура типа T1 является собственным подмножеством cv-квалификационная подпись типа T2.

      или, если это не так,

      • S1 и S2 являются привязками привязки (8.5.3), и ни один из них не ссылается на неявный параметр объекта нестатическая функция-член, объявленная без ref-quali fi, и S1 связывает ссылку rvalue с rvalue и S2 связывает ссылку lvalue.