Двойной стандарт? Почему только предупреждение для char * const & a = "bla"? - PullRequest
0 голосов
/ 30 августа 2018

После попытки немного углубиться в механику таких случаев, как , этот вопрос выявляется, я все еще не понимаю, почему третья строка в коде ниже генерирует только предупреждение, а вторая - это ошибка.

int main()
{
    const char* const& a = "bla"; // Valid code
    const char*& a2 = "bla"; // Invalid code
    char* const& a3 = "bla"; // Should be invalid but settles for a warning

    return 0;
}

Я знаю, что в то время как инициализация ссылки преобразует строковый литерал в ссылку на указатель, тогда он не должен сбрасывать любые cv-qualifiers объект имеет, и поскольку преобразованный тип равен const char* const (преобразован из строкового литерала "bla", то есть const char[4]), он, похоже, имеет тот же случай, что и вторая строка. Единственное отличие состоит в том, что удаляемый const принадлежит самой строке C, а не указателю.

Воспроизводится как в GCC 8.2, так и в Clang 6.0.0 без указания каких-либо дополнительных флагов соответствия.

Вывод из gcc :

<source>:4:23: error: cannot bind non-const lvalue reference of type 'const char*&' to an rvalue of type 'const char*'
     const char*& a2 = "Some other string literal";
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~

<source>:5:23: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
     char* const& a3 = "Yet another string literal";

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

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Оба случая плохо сформированы. Тем не менее, стандарт не требует, чтобы компиляторы отказывались от некорректных программ. Таким образом, согласие на предупреждение полностью соответствует стандарту.

Или, альтернативно, есть ли принципиальное различие, которое я здесь упускаю между двумя случаями?

Основным отличием является то, что привязка неконстантной lvalue-ссылки к r-значению никогда не была правильно сформирована, в то время как неявное преобразование const char* в char* раньше было правильно сформировано до C ++ 11. Обратная совместимость является хорошим аргументом для разрешения последнего.

0 голосов
/ 31 августа 2018

Давайте разберем это, используя константный синтаксис EAST.

Правило const состоит в том, что оно всегда применяется к тому, что находится слева от него, если только от него ничего нет, в этом случае оно применяется к тому, что находится непосредственно справа. С EAST const мы всегда пишем const справа.

Итак, давайте посмотрим на код:

const char* const& a = "bla"; // Valid code

становится

char const * const & a = "bla";

так что символ постоянен и не может быть изменен.

Указатель на символ постоянен и не может быть изменен.

В целом: это ссылка на указатель, который нельзя изменить на символ, который нельзя изменить.

"bla" - это массив const C-style, который сразу распадается на char const * const.

Причина, по которой он является «char const * const», а не «char const *», заключается в том, что адрес «bla» является постоянным - строка «bla» компилируется в код выполнения где-то в фиксированном месте, и когда загруженный в память будет оставаться на этом адресе памяти до завершения программы.

Так что теперь у нас есть подходящие типы, кроме ссылки.

T & a = что-то; всегда будет работать, если что-то имеет тип T, а у чего-то есть адрес (это так).

Давайте посмотрим на второй:

const char*& a2 = "bla"; 

Синтаксис EAST const:

char const * & a2 = "bla";

«бла» относится к типу:

char const * const

Это не совпадающие типы (место памяти "bla" фиксировано).

Может быть, этот код сделает его более понятным:

char const *stringPtr = "hello";
char const *stringPtr2 = "world";

char const * &stringPtrRef = stringPtr;

std::cout << stringPtr << std::endl;

stringPtrRef = stringPtr2;

std::cout << stringPtr << std::endl;

Будет напечатано «Hello» в первой строке и «World» во второй. Это потому, что stringPtr указывает на изменения.

Поскольку местоположение «bla» является фиксированным, мы не можем создать ссылку на него, где местоположение «bla» можно изменить, установив ссылку на него на что-то другое. Это просто невозможно. Нет также никакого возможного броска, который мы могли бы использовать, чтобы заставить его стать правильным типом.

Вот почему он не может компилироваться даже с предупреждениями.

Давайте посмотрим на третий:

char* const& a3 = "bla";

Это уже в формате EAST const.

с "char * const &" - ​​результирующая ссылка, не позволяя изменить расположение в памяти, позволит вам изменить "bla" на "abc".

Возможно, в некоторых случаях вы действительно хотите сделать это, чтобы сэкономить пространство памяти в некоторых встроенных системах, где «bla» использовался только в качестве инициализации и никогда больше.

Сообщение имеет смысл:

"предупреждение: ISO C ++ запрещает преобразование строковой константы в 'char *"

потому что это по сути то же самое, что и

char const *s1 = "bla";
char *s2 = s1;

, который фактически скомпилируется с предупреждением с правильными флагами компилятора (-fpermissive).

Даже без -fpermissive мы могли бы изменить код, чтобы выполнить приведение и заставить его работать.

Итак, я понимаю, почему он может компилироваться, но я думаю, что это должно быть ошибкой. ISO C ++ явно запрещает это. Мое мнение: Требуй актерский состав, если это действительно то, что ты хочешь сделать.

0 голосов
/ 30 августа 2018

Строковые литералы являются массивами. Тип "bla" является const char [4].

const char* const& a = "bla";

Это действительно, потому что есть преобразование из T [] в T *; в этом случае вы получите const char * значение. Это значение может быть привязано к ссылке, потому что это ссылка на const (которая поддерживает временные данные и т. Д.).

const char*& a2 = "bla";

Неверно, потому что здесь вы пытаетесь привязать временное значение к неконстантной ссылке.

char* const& a3 = "bla";

Это ссылка на const, но неправильного типа (это указатель на символ, а не указатель на символ-констант). Это преобразование отбрасывает квалификатор const, поэтому оно должно быть недействительным. Некоторые компиляторы C ++ допускают это по причинам обратной совместимости: в строковых литералах C тип не имеет константного значения (т. Е. "bla" будет char [4]), поэтому из-за серьезной ошибки это может привести к поломке большого количества существующего кода.

Даже в C ++ это было законно. До C ++ 11 назначение строкового литерала переменной char * (не const char *) все еще было разрешено (но не рекомендуется).

«Двойной стандарт» заключается в том, что привязка неконстантной ссылки к временному никогда не допускалась (C даже не имеет ссылок), поэтому там нет проблем обратной совместимости. Стандарт не различает «ошибки» и «предупреждения»; по усмотрению автора компилятора должна ли компиляция завершиться успешно при любом нарушении правил.

...