Почему ссылки не "const" в C++?


мы знаем ,что "переменная const" указывает, что после назначения вы не можете изменить переменную, например:

int const i = 1;
i = 2;

программа выше не будет компилироваться; GCC запрашивает с ошибкой:

assignment of read-only variable 'i'

нет проблем, я могу понять это, но следующий пример находится за пределами моего понимания:

#include<iostream>
using namespace std;
int main()
{
    boolalpha(cout);
    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;
    int const &ri = i;
    cout << is_const<decltype(ri)>::value << endl;
    return 0;
}

выводит

true
false

странно. Мы знаем, что как только ссылка привязана к имени / переменной, мы не можем изменить эту привязку, мы измените его Связанный объект. Поэтому я предполагаю, что тип ri должно быть то же самое, что i: для i это int const, почему ri не const?

6 84

6 ответов:

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

это кажется логичным для указатель:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

выход:

true
false

это логично, потому что мы знаем, что это не указатель на объект, что является константой (это может быть сделано в точка в другом месте) это объект, на который указывают.

так мы правильно видим constness на указатель возвращены false.

если мы хотим сделать указательconst мы говорим:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const* const ri = &i;
    cout << is_const<decltype(ri)>::value << endl;
}

выход:

true
true

и поэтому я думаю, что мы видим синтаксическую аналогию с ссылка.

однако ссылки семантически отличаются от указателей, особенно в один решающее уважение, нам не позволено rebind ссылка на другой объект после привязки.

так что даже если ссылки использовать тот же синтаксис, что и указатели правила разные и поэтому язык не позволяет нам объявлять ссылкаconst такой:

int main()
{
    boolalpha(cout);

    int const i = 1;
    cout << is_const<decltype(i)>::value << endl;

    int const& const ri = i; // COMPILE TIME ERROR!
    cout << is_const<decltype(ri)>::value << endl;
}

я полагаю, что мы не разрешается делать это, потому что это не кажется необходимым, когда правила языка предотвращают ссылка от того, чтобы отскочить таким же образом a указатель может(если он не объявлен const).

Итак, чтобы ответить на вопрос:

Q) почему " ссылка "не является" const " в C++?

в вашем примере синтаксис делает вещь, на которую ссылаются const так же, как если бы вы были объявления указатель.

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

int const& const ri = i; // not allowed

Q) мы знаем, что как только ссылка привязывается к имени/переменной, мы не можем изменить эту привязку, мы меняем ее привязанный объект. Поэтому я предполагаю, что тип ri должно быть таким же, как i: для i это int const, почему ri не const?

почему decltype() не переносится на объект referece обязан?

я полагаю, что это для семантической эквивалентности с указатели и, возможно, также функция decltype() (объявленный тип) - это оглянуться назад на то, что было объявил до того, как произошло связывание.

почему "ri "не"const"?

std::is_const проверяет, является ли тип const-квалифицированным или нет.

если T является типом с константой const (то есть const или const volatile), то значение константы члена равно true. Для любого другого типа, значение false.

но ссылка не может быть const-квалифицированные. $8.3.2/1 ссылки [dcl.ref]

Cv-квалифицированные ссылки плохо сформированы, за исключением случаев, когда cv-квалификаторы вводятся с помощью typedef-name ([dcl.оператор typedef], [температура.param]) или decltype-спецификатор ([dcl.тип.простой]), в этом случае в CV-квалификаторы игнорируются.

так is_const<decltype(ri)>::value вернутся false, потому что ri (ссылка)не является константным типом. Как вы сказали, Мы не можем повторно привязать ссылку после инициализации, что подразумевает, что ссылка всегда "const", с другой стороны, const-квалифицированная ссылка или const-неквалифицированная ссылка может не иметь смысла на самом деле.

вы должны использовать std::remove_reference для получения значения, которое вы ищете.

std::cout << std::is_const<std::remove_reference<decltype(ri)>::type>::value << std::endl;

для получения дополнительной информации см. раздел этот пост.

почему макросы не const? Функции? Литералы? Имена типов?

const вещи - это только подмножество неизменяемых вещей.

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

если C++ имел неизменяемые объекты по умолчанию, требующие mutable сайта на не хочу быть const, тогда это было бы легко: просто не позволяйте программистам добавлять mutable для ссылочных типов.

как бы то ни было, они неизменны без оговорок.

и, поскольку они не являются const-квалифицированный, это, вероятно, будет больше заблуждение is_const на ссылочном типе для получения true.

Я считаю, что это разумный компромисс, тем более, что неизменность является во всяком случае, это связано с тем, что синтаксис не существует для изменения ссылки.

это причуда / функция в C++. Хотя мы не думаем о ссылках как о типах, они фактически "сидят" в системе типов. Хотя это кажется неудобным (учитывая, что при использовании ссылок семантика ссылок происходит автоматически, и ссылка "уходит с пути"), есть некоторые оправданные причины, по которым ссылки моделируются в системе типов, а не как отдельный атрибут вне типа.

во-первых, давайте рассмотрим, что не каждый атрибут объявленного имя должно быть в системе типов. Из языка C у нас есть "класс хранения" и "связь". Имя может быть введено как extern const int ri, где extern указывает на статический класс хранения и наличие связи. Тип просто const int.

в C++, очевидно, охватывает понятие, что выражения имеют атрибуты, которые находятся вне системы типов. Язык теперь имеет понятие "класс значений", который является попыткой организовать растущее число атрибутов не-типа, которые выражение может проявляться.

тем не менее ссылки являются типами. Зачем?

раньше в учебниках по C++ объяснялось, что объявление типа const int &ri ввел ri как типа const int, но ссылочная семантика. Эта ссылочная семантика не была типом; это был просто своего рода атрибут, указывающий на необычную связь между именем и местом хранения. Кроме того, тот факт, что ссылки не являются типами, использовался для обоснования того, почему вы не можете создавать типы на основе ссылок, даже если синтаксис конструкции типа позволяет это. Например, массивы или указатели на ссылки невозможны:const int &ari[5] и const int &*pri.

но ведь ссылки are типы и так decltype(ri) извлекает некоторый узел ссылочного типа, который является неквалифицированным. Вы должны спуститься мимо этого узла в дереве типов, чтобы добраться до базового типа с помощью remove_reference.

при использовании ri, ссылка прозрачно разрешена, так что ri "смотрит и чувствует как i "и может быть назван" псевдоним " для него. В системе типов, однако, ri на самом деле есть типа "ссылкаconst int".

почему типы ссылок?

считаю, что если ссылки не типы, тогда эти функции будут считаться однотипными:

void foo(int);
void foo(int &);

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

аналогично, если бы ссылки не были типами, то эти два объявления классов были бы эквивалентны:

class foo {
  int m;
};

class foo {
  int &m;
};

было бы правильно, чтобы одна единица перевода использовала одно объявление, а другая единица перевода в той же программе использовала другое декларация.

дело в том, что ссылка подразумевает разницу в реализации и это невозможно отделить от типа, потому что тип в C++ имеет отношение к реализации сущности: его "макет" в битах, так сказать. Если две функции имеют один и тот же тип, они могут быть вызваны с одинаковыми соглашениями о двоичном вызове: ABI-то же самое. Если две структуры или классы имеют один и тот же тип, их расположение одинаково, а также семантика доступа к все члены. Наличие ссылок изменяет эти аспекты типов, и поэтому это простое дизайнерское решение, чтобы включить их в систему типов. (Однако обратите внимание на контраргумент здесь: член struct / class может быть static, который также изменяет представление; но это не тип!)

таким образом, ссылки находятся в системе типов как "граждане второго класса" (в отличие от функций и массивов в ISO C). Есть определенные вещи, которые мы не можем "делать" со ссылками, например как объявить указатели на ссылки, или массивы из них. Но это не значит, что они не типы. Они просто не являются типами в том смысле, что это имеет смысл.

не все эти второсортные ограничения являются существенными. Учитывая, что есть структуры ссылок, могут быть массивы ссылок! Е. Г.

// fantasy syntax
int x = 0, y = 0;
int &ar[2] = { x, y };

// ar[0] is now an alias for x: could be useful!

это просто не реализовано в C++, вот и все. Указатели на ссылки вообще не имеют смысла, потому что указатель, поднятый из ссылки, просто идет к объекту ссылки. Вероятная причина, по которой нет массивов ссылок, заключается в том, что люди C++ считают массивы своего рода низкоуровневой функцией, унаследованной от C, которая сломана во многих отношениях, которые непоправимы, и они не хотят касаться массивов в качестве основы для чего-либо нового. Существование массивов ссылок, однако, сделало бы ясный пример того, как ссылки должны быть типами.

неconst-квалифицируемые типы: найдено в ISO C90, тоже!

некоторые ответы намекая на то, что ссылки не const квалификатор. Это скорее отвлекающий маневр, потому что декларация const int &ri = i не попытка сделать const-квалифицированная ссылка: это ссылка на const-квалифицированный тип (который сам по себе не const). Так же, как const in *ri объявляет указатель на что-то const, но этот указатель сам по себе не const.

тем не менее, это правда, что ссылки не могут нести const сами отборочные.

но это не так странно. Даже в языке ISO C 90 не все типы могут быть const. А именно, массивов быть не может.

во-первых, синтаксис не существует для объявления массива const:int a const [42] является ошибочным.

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

typedef int array_t[42];
const array_t a;

но это не делает то, что кажется. В этой декларации это не так a что получает const квалифицированный, но элементы! То есть, a[0] это const int, а a это просто "массив int". Следовательно, это не требует диагностики:

int *p = a; /* surprise! */

это:

a[0] = 1;

опять же, это подчеркивает идею о том, что ссылки в некотором смысле являются "вторым классом" в системе типов, например массивы.

обратите внимание, как аналогия проходит еще глубже, так как массивы также имеют " невидимое поведение преобразования", как ссылки. Без использования программистом какого-либо явного оператора идентификатор a автоматически превращается в int * указатель, как будто выражение &a[0] были использованы. Это аналогично тому, как ссылка ri, когда мы используем его в качестве основного выражения, магически обозначает объект i, к которому он привязан. Это просто еще один" распад", как"массив для распада указателя".

и так же, как мы не должны запутаться в "массив к указателю" распадаясь на неправильное мышление ,что " массивы-это просто указатели в C и c++", мы также не должны думать, что ссылки-это просто псевдонимы, которые не имеют собственного типа.

, когда decltype(ri) подавляет обычное преобразование ссылки на объект референт, это не так уж отличается от sizeof a подавление преобразования массива в указатель и работа с тип массива чтобы рассчитать его размер.

const X& x " означает, что X псевдонимы объекта X, но вы не можете изменить этот объект X через x.

и видим std:: is_const.