Почему ссылки не "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 ответов


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

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

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

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

поэтому мы правильно видим константность на указатель возвращены 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;
}

я полагаю, что мы не разрешается делать это, потому что это не кажется необходимым, когда языковые правила предотвращают ссылка от отскока таким же образом указатель может (если он не объявлен 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-квалифицированная ссылка или 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-qualifiable типы: найдено в 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.