Разрешение перегрузки и массивы: какую функцию следует вызывать? - PullRequest
29 голосов
/ 18 марта 2011

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

#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 C ++ 2010 SP1
  • (3) вызывается, когда программа компилируется с использованием Clang 3.0 (транк 127530)

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

Ответы [ 3 ]

12 голосов
/ 18 марта 2011

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

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

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

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

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

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


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

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

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

Если мы предположим, что временное значение рассматривается как r-значение в соответствии с пунктом 13, то мы привязываем r-значение ref к r-значению во второй функции и l-значению в первой.Поэтому мы выберем вторую функцию, а затем получим диагностику по последнему пункту 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,Так что, похоже, есть некоторая путаница - я отправил ДР в комитет по этому поводу.

4 голосов
/ 18 марта 2011

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

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

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

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

, как ни странно, Комо предпочитает (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;
    }
}
2 голосов
/ 18 марта 2011

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

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

  1. Определите 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, если для всех аргументов i , 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.
  2. Если существует ровно одна жизнеспособная функция, которая лучше, чем все другие жизнеспособные функции, то она выбирается по разрешению перегрузки; в противном случае вызов неверен.

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

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

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

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

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

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

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

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

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

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

      • S1 является правильной подпоследовательностью S2 (сравниваяпоследовательности nversion в канонической форме, определенной в 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, и S1 связывает ссылку rvalue с rvalue, а S2 связывает ссылку lvalue.
...