Почему ссылки не "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
это логично, потому что мы знаем, что это не указатель на объект, что является константой (это может быть сделано в точка в другом месте) это объект, на который указывают.
так мы правильно видим 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.