Правильно ли это разрешение перегрузки?

От: Можно ли перегрузить char * и std::string?

#include <string>
#include <iostream>
void foo(std::string str) {
  std::cout << "std::string\n";
}

void foo(char* str) {
  std::cout << "char*\n";
}

int main(int argc, char *argv[]) {
  foo("Hello");
}

Вышеприведенный код печатает "char *" при компиляции с помощью g++-4.9.0 -ansi -pedantic -std=c++11.

Я считаю, что это неверно, потому что тип строкового литерала является "массивом n const char", и с ним не должно быть возможности инициализировать не const char*, поэтому std::string следует выбрать перегрузку. Является ли gcc нарушением стандарта здесь?

Ответ 1

Во-первых, тип строковых литералов: все они являются постоянными массивами их типа символов.

2.14.5 Строковые литералы [lex.string]

7 Строковый литерал, начинающийся с u8, такой как u8 "asdf", является строковым литералом UTF-8 и инициализируется заданными символами, закодированными в UTF-8.
8 Обычные строковые литералы и строковые литералы UTF-8 также называются узкими строковыми литералами. Узкий строковый литерал имеет тип "массив из n const char", где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения (3.7).
9 Строковый литерал, начинающийся с u, такой как u "asdf", является строковым литералом char16_t. Строковый литерал char16_t имеет тип "array of n const char16_t", где n - размер строки, как определено ниже; он имеет статическую продолжительность хранения и инициализируется заданными символами. Один c- char может генерировать более одного символа char16_t в виде суррогатных пар.
10 Строковый литерал, начинающийся с U, такой как U "asdf", является строковым литералом char32_t. Строковый литерал char32_t имеет тип "array of n const char32_t", где n - размер строки, как определено ниже; он имеет статическую продолжительность хранения и инициализируется заданными символами.
11 Строковый литерал, начинающийся с L, такой как L "asdf", является строковым литералом. Широкий строковый литерал имеет тип "array of n const wchar_t", где n - размер строки, как определено ниже; он имеет статическую продолжительность хранения и инициализируется заданными символами.

Далее, давайте увидим, что мы имеем только стандартный распад массива, поэтому от T[#] до T*:

4.2 Преобразование массива в указатель [conv.array]

1 lvalue или rvalue типа "массив из N T" или "массив неизвестной границы T" можно преобразовать в prvalue типа "указатель на T". Результатом является указатель на первый элемент массива.

И, наконец, давайте увидим, что любое соответствующее расширение не должно изменять значение правильной программы:

1.4 Соответствие требованиям [intro.compliance]

1 Набор диагностируемых правил состоит из всех синтаксических и семантических правил в этом Международном стандарте, за исключением тех правил, которые содержат явное обозначение, что "никакой диагностики не требуется" или которые описаны как результат "undefined поведения". < ш > 2 Хотя в этом международном стандарте утверждены только требования к реализациям на С++, эти требования часто легче понять, если они сформулированы как требования к программам, частям программ или выполнению программ. Такие требования имеют следующее значение:

  • Если программа не содержит нарушений правил в этом Международном стандарте, соответствующая реализация должна в пределах своих ресурсов принимать и правильно выполнять эту программу.
  • Если программа содержит нарушение любого диагностируемого правила или появление конструкции, описанной в этом стандарте, как "условно поддерживаемая", когда реализация не поддерживает эту конструкцию, соответствующая реализация должна выпустить хотя бы одно диагностическое сообщение.
  • Если программа содержит нарушение правила, для которого не требуется диагностика, этот Международный Стандарт не требует каких-либо требований к реализации в отношении этой программы.

Итак, в итоге, это ошибка компилятора.

(До С++ 11 (С++ 03) преобразование было разрешено, но устарело, поэтому было бы правильно. Диагностика в случае, если это произошло, не требовалось бы, а предоставлялось как проблема качества реализации.)

Это ошибка GCC, а также ошибка clang.
Отчет об ошибке для clang: http://llvm.org/bugs/show_bug.cgi?id=16314 (Благодаря TC для поиска отчета об ошибке.)

Тест файл из отчета об ошибке clang, который намного короче:

void f(char*);
int &f(...);
int &r = f("foo");