Value category c что это
Value category
A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
A prvalue is an expression whose evaluation initializes an object or a bit-field, or computes the value of an operand of an operator, as specified by the context in which it appears, or an expression that has type cv void.
Получить текущий locale. (category, * locale)
Привет! Если стоит: setlocale (LC_ALL, «Ukrainian»); как я могу теперь получить его аргументы.
If (!isset($category) <>
всем привет! чет не могу понять в чем прикол. 7
Чукча не читатель, чукча писатель? В стандарте примерно так и написано.
Добавлено через 1 минуту
Haskell + Category theory
Слышал что JavaScript это Haskell + Category theory 🙂
Wp не видит category.php и т.д
Есть custom тип записей. А что делать есть wp не видит category.php, category-36.php и т.д. А.
ЧПУ: /category/product
Как изменить http://site/index.php?category=bra&product=Push-Up%20Bra На.
Как избавиться от /category/ в url
добрый день, помогите пожалуйста решить проблему с категориями, или подскажите наиболее.
Kernel-Power 41 Task Category 63
Привет! Вынужден обратиться за помощью в связи с такой проблемой: Периодически мой компьютер.
Value categories – [l, gl, x, r, pr]values
So you have heard about lvalues and rvalues for sure, but there actually are also xvalues, prvalues and glvalues. It’s easy to get lost in it, so let’s have a look at what actually are those things.
types of expression according to the standard
The picture shows the division of the value categories defined in the draft standard. In this particular case, it’s here
So, first of all, we can see, that glvalue and rvalue are nothing more than the containers for more concrete value categories. My guess terms glvalues and rvalues were added to the standard for convenience to avoid repeating words like “ lvalue and xvalue” and “xvalue and prvalue”.
Glvalues
Let’s first focus on the glvalues and let’s understand what this actually means, that the expression is a glvalue. So first what actually standard says about the glvalues is that glvalues expressions evaluation determines the identity of an object. In other words, it means, that results of such expressions refer to the concrete object (those are just references to the objects).
How I like to think about about glvalues is: if expression returns reference (T&), or refers to the variable by it’s name, or rvalue reference (T&&) to the object it’s a glvalue expression. Let’s see examples of the glvalues. For the given definitions:
Following expressions are glvalues:
But glvalues are divided into lvalues and xvalues, so let’s first see what xvalues are since lvalues are defined as all glvalues which are not xvalues 🙂
So what xvalue means in practice? Well, xvalue used to be an lvalue, but for some reason, it was marked, that its resources can be reused or in other words, the object should be moved instead of copied. That kind of additional marking to the type can be achieved when calling std::move function on the object. Another way of getting the xvalue is through temporary materialization conversion, but we will talk about it in the conversion section.
One more time my way of thinking about xvalues is, that if expression returns type of rvalue reference (T&&), then expression is xvalue. We can get expression of xvalue category type with following rules:
(offtopic) : If you didn’t have a possibility to see pointers to members in action, then this syntax must be a pain in your eyes. My apologies for that. What you need to know beside the syntax itself is that pointer to member is actually a simple offset from the beginning of the object to the actual member. Thus there is no need for a concrete object existence (only definition is needed) before saving this pointer to the variable.
Some other examples from the standard about xvalues can be found below, where for given definitions:
Following expressions are xvalues:
So once we know what glvalues are and what xvalues are we also know what lvalues are, since it’s all what is left after subtracting glvalues and xvalues sets. Meaning, that lvalue expressions, are expressions, that yield lvalue references (T&) or refer (by name) to an existing variable.
As a summary, we should say, that all expressions, that are glvalues actually yield a handle to some variable with some value. Thus it’s not possible to have the expression of glvalue type, that would have (const volatile) void type (it’s not possible to have object of type void). Glvalues are references to the objects.
Rvalues
Rvalues as can be seen on the image is a set containing all xvalues and also prvalues. Since we know what xvalues are, we need to have a look at prvalues a little bit closer.
I think it’s best to explain the prvalues on the examples.
Type completeness requirements
The glvalues types (lvalue and xvalue) have different characteristics than the prvalues. Because of their nature (being the handle of the object) the expression can yield a value of incomplete type. Since prvalues are values itself they need to have a complete type (or void type). Let’s consider the following example, where the function does not need a complete type at all.
The above function does not need a complete definition of type T, that it operates on. Let’s consider following usage of the function:
The whole program compiles and can be run even though in the main translation unit the type foo is not complete. It’s possible because nowhere in the program foo is used as prvalue plus there are no operations directly invoked on the foo type, that would require it’s definition.
Let’s consider the same example, but with changed return type of the first_value template function:
Now the return value of the first_value function is prvalue, since it’s not a handle to any of the objects. The program now will fail to compile as the prvalue expressions need complete definition of the type they yield.
On my environment the compilation results in following message:
Expression types conversions
To make developers life easier, there are lots of conversions defined, when types of expressions do not match regarding its value category. The conversions, that will apply might be from glvalue to prvalue expressions or prvalue to xvalue conversions
Glvalue to prvalue conversion
Glvalue to prvalue happens very often and there are many such conversions defined in the C++. They are:
Array to pointer conversion:
First of all, the conversion does not apply to the array and function types as this would otherwise conflict the array to pointer and function to pointer conversions. Another precondition for such conversion is, that when such conversion takes place, the T must be a complete type. This requirement comes from already mentioned prvalues expression requirements. Since prvalue represents the value, the complete type of this value must be known.
Once the preconditions are met the conversion is following:
I personally think it’s the most often used conversion (no statistics about it) and usually noone really is aware of this conversion. Let’s see the example of the expression conversion:
Looking at above example it’s easy to realize, that almost every function call in the program relies on the lvalue to rvalue conversion.
Prvalue to xvalue conversion.
There is also vice versa conversion, that is applied if you are trying to pass the prvalue in the place, where you are actually expecting the glvalue. In such a case the conversion is not enough, because except for the type conversion there must be object instantiation, since glvalue needs to refer to the object.
Such C++ feature, that actually does the object instantiation and casts prvalue type to the xvalue (that belongs to the glvalue group) is called temporary materialization conversion.
When needed, the temporary materialization conversion creates a temporary variable, that is initialized with the value, that prvalue expression holds, and marks that temporary variable as a xvalue. The reason its xvalue is because the temporary variable will soon expire, thus it’s resources are ready to be reused. Thanks to this conversion we can do things like:
We can see that in action in detail when we try to get the address of the variable. Let’s see the example. If we have got a prvalue expression, we cannot take the address of the result of such expression, because there is no variable, that stands behind the value ( it’s a temporary ). In the following example, even if conversion is applied, the program will still be ill-formed, since you can take the address of the lvalue only (xvalue is not enough)
In such case clang produces following error:
But we can take address of the result of such expression after temporary materialization conversion:
Expression types and bit fields
The bit fields are a bit special in the C++ world. As you cannot take the pointer to the bit-field itself. That’s something natural as bitfields can occupy less memory than one byte, which is the smallest addresable unit in C++ world.
But the fact that you cannot take the address to the bit-field doesn’t mean you cannot have the glvalue expression to the bit field.
This example also shows, that expression category does not always have one to one correlation to the type they return.
That’s also one of few differences between the pointers and references 🙂 it’s not possible to take the pointer to the bitfield, be it const or not, but you can take a const reference to the bitfield.
So you can see, that you can have lvalue expression pointing to the bitfield, since you can assign to them. It’s not on the other hand possible to save the reference to the bitfield to the variable manually. This is the slight difference between the expression type and type itself.
Популярные заблуждения о C#
Эта статья является развёрнутым комментарием к другой статье. Обычно я прохожу мимо, но сейчас меня почему-то задело.
Та статья представляла из себя практически «идеальную подборку заблуждений в вакууме». Причём они (заблуждения) являются довольно популярными и постоянно встречаются в различных блогах и подборках «99 вопросов для собеседования», «как пройти собеседование на джуниора» или в данном случае «шпаргалка по C#».
Почему они такие популярные? Я считаю, что потому, что они дают простые и короткие ответы, которые очень удобны для формата теста или квиза. Думать не надо, можно просто запомнить. Практикующие же разработчики повторяют их потому что эти заблуждения часто описывают наблюдаемое положение дел, но это не значит, что они истинны. Если посмотреть в окно в центре города, то можно сделать вывод что все машины — легковые, и это в большинстве случаев будет верно, но к правде имеет довольно слабое отношение.
Мне хочется, чтобы данная статья была не чисто моим мнением и не очередной компиляцией блогов. Поэтому я проведу аналогию (весьма сомнительную, но мне так захотелось) с юриспруденцией. У нас будет так:
Законопроекты — информация о версиях языка новее, чем в ECMA 334, а также ещё не выпущенных версиях. Эту информацию можно найти на гитхабе dotnet, а так же в статьях-анонсах на MSDN.
Подзаконные акты — документация (не статьи) MSDN.
Опыт законоприменения — статьи MSDN, Wikipedia и на других сайтах, информация не абсолютная, требует проверки.
Прямой опыт — то, что мы можем просто взять и проверить. Тут нам повезло больше, чем юристам, ведь нам не придётся что-то красть, чтобы проверить на сколько лет за это посадят ))
Не долго думая, возьмём план статьи отсюда и будем рассматривать мифы по списку. Если что-то забуду, добавляйте в комментах.
Ссылочные и значимые типы (value vs reference types)
Заблуждение: ссылочные типы (reference type) хранятся в куче (heap), а значимые (value types) — на стэке.
Почему распространено? Это — простое объяснение, и оно часто тиражируется. Более того, если написать простой метод с простыми переменными одного и другого типа, чем обычно его и иллюстрируют, то всё именно так и будет.
Закон: во всём стандарте есть только 2 упоминания слова «куча», и это вполне объяснимо, поскольку стэк и куча являются деталями реализации, а не самого языка. Второе упоминание — про то, что зафиксированные (fixed) объекты могут приводить к фрагментации кучи, это нам пока не интересно. А первое упоминание — в разделе 16.1 Structs/General, то есть общем описании, а не определении:
However, unlike classes, structs are value types and do not require heap allocation
Это означает только то, что структуры не требуют выделения на куче (но не означает что они располагаются на стэке).
Опыт: действительно, значимые типы, являющиеся локальными переменными в текущей реализации скорее всего окажутся на стэке. Но если мы создадим класс, членом которого является структура, то она, как и все остальные данные этого класса окажется в куче.
Для классов (ссылочных типов) на текущий момент действительно верно, что во всех простых случаях они окажутся размещёнными в куче. Но ссылочный тип чисто теоретически можно разместить на стэке. Более того, скоро это изменится вполне официально, так же спасибо @VladD-exrabbit за вот эту ссылку: https://github.com/dotnet/runtime/issues/11192
P.S.: ещё спасибо @PsyHaSTe за ссылки: один, два, три. «Как можно видеть, Липперта эти сравнения бесили ещё в 2009. »
Так чем же отличаются value и reference типы?
Читаем стандарт (в нём всё про значимые типы лежит в разделе Structs и слово struct используется для их описания):
— A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to an object that contains the data…
Тут текст про то, что структуры содержат в себе сами данные, а переменная со ссылочным типом — только ссылку. А значит структура не может в себе содержать поля, размер которых ещё не известен (в том числе своего же типа):
With classes, it is possible for two variables to reference the same object…
Переменные ссылочного типа могут (но не обязаны) указывать на один и тот же объект, а каждая значимая переменная содержит свою копию данных.
Это всё, что описано в разделе про семантику, а для языка это самое главное. Дальше идёт описание конкретно структур (16.4.3 Inheritance) а также свойства, вытекающие из семантики (16.4.4 Assignment — копирование данных при присваивании), 16.4.5 Default values — значение по умолчанию, 16.4.6 Boxing and unboxing — если нам надо передать ссылку, то требуется боксинг. А так же конструкции языка 16.4.7 Meaning of this, 16.4.8 Field initializers, 16.4.9 Constructors, 16.4.10 Static constructors, 16.4.11 Automatically implemented properties.
На собеседовании на вопрос о различии этих типов главное ответить, что у них разная семантика. Можно, конечно, погрузиться в дальнейшие различия (по списку из стандарта), но нигде среди них нет упоминания, что одно — это то, что идёт на стэк, а другое — в кучу.
stack vs heap
Заблуждение (1): стэк быстрый, а куча большая.
Почему? Потому что мелкие локальные переменные, такие как числа, обычно располагаются на стэке, а жирные объекты размещают в куче. Очевидно, что с мелкими объектами, которые известно где лежат, работать проще и быстрее. Но это скорее следствие, чем причина.
Закон: слово heap мы уже искали в стандарте и ничего серьёзного не нашли. Слово stack в основном встречается в параграфах про unsafe-блоки и stackalloc, но мы сейчас не про это.
Факт: размещать данные на куче действительно сложнее (нужно пройти через аллокатор памяти), а на стэке — быстрее (делать ничего не надо, уже известно где и сколько памяти мы используем). Но дальнейший доступ будет аналогичен (если и то и другое мы будем трогать одинаковым способом, например по ссылке) и скорость доступа будет зависеть только от эффектов локальности данных в кэше, но это уже не вопрос языка, а физической машины.
Размер стэка можно поменять для всего бинарника с помощью EDITBIN.EXE /STACK: file.exe
А для каждого отдельного потока — через второй аргумент конструктора new Thread().
По умолчанию, куча действительно больше стэка, но стэк можно увеличить, а куча, хоть и растёт по мере надобности, но не безгранична. Есть куча правил, по которым определяется её размер, так же её можно уменьшить в настройках. Иногда доступная куча оказывается меньше физически доступной памяти Тогда приходится расчехлять unsafe и делать offheap-аллокации.
Вывод: размер и того и другого определяются машиной и рантаймом, но не является определяющим признаком.
Передача по значению / указателю
Заблуждение: значимые типы (структуры) передаются по значению а ссылочные (классы) — по ссылке.
Почему оно популярно? Честно — не знаю. Возможно из курсов Си для начинающих.
Закон:
10.2.5 Value parameters
A parameter declared without a ref or out modifier is a value parameter.
10.2.6 Reference parameters
A parameter declared with a ref modifier is a reference parameter.
10.2.7 Output parameters
A parameter declared with an out modifier is an output parameter.
Думаю, тут всё понятно. То, как передаётся объект, определяется не его типом (ссылочный/значимый) а тем, как объявлен и передан аргумент функции. Добавляется ещё странная вещь под названием Output parameter со своей семантикой, но в реализации это такой же ref-параметр, только требующий инициализации в вызываемом методе. На этом дискуссию можно было бы закончить, но давайте немного займёмся сравнительным языкознанием.
Для сравнения я возьму Java 8-летней давности (в последний раз что-то значимое на джаве я писал примерно тогда, а с тех пор могло что-то поменяться), С++ (а не C, потому что иначе я сам запутаюсь) и C#. Я хотел тут повторить анализ целиком, но просто приведу ссылки: Java, C++, C#.
Краткий пересказ: в Java передача только по значению, но есть ссылочные типы и примитивы (это не совсем значимые типы как в C#, но для сравнения сойдёт) В C++ все типы — значимые, но можно передавать как по значению так и по ссылке (и ещё по указателю/адресу). А в C# сочетаются обе эти семантики: можно взять значимый или ссылочный тип и передать любой из них по ссылке или по значению. Это ортогональные понятия и не надо их смешивать.
P.S.: в новых версиях языка появились in-параметры. С семантической точки зрения они не определяют способ передачи (ведь при запрете изменения объекта нет никакой разницы, как он был передан), но с точки зрения реализации они работают как неизменяемые ref-параметры (readonly ref) и соответственно тоже передаются по ссылке.
P.P.S.: с out-параметрами тоже не всё так просто. Вот в этой статье есть подробный разбор: https://m.habr.com/ru/company/pvs-studio/blog/542210/, рекомендую к прочтению.
string — особенный тип
Заблуждение: ведёт себя как значимый тип, а лежит в куче.
Закон:
9.2.5 The string type
The string type is a sealed class type that inherits directly from object. Instances of the string class represent Unicode character strings.
Values of the string type can be written as string literals (§7.4.5.6).
The keyword string is simply an alias for the predefined class System.String
Как видим, не такой уж он и особенный.
Опыт: ну да, как и все другие классы (ссылочные типы) строки обычно размещаются в куче. Почему говорят, что он ведёт себя как значимый тип? Я много раз такое слышал, но так и не получил чёткого ответа, почему.
Про какие особенности речь?
1. Это неизменяемый (immutable) и запечатанный (sealed) класс. Это значит, что обычными способами нельзя изменить внутри него данные и нельзя от него унаследоваться. Ну и что? Вы можете создавать классы с такими же ограничениями, ничего особенного.
2. Можно сравнивать с помощью оператора==, а обычные структуры нельзя, и для голых классов сравнивается инстанс, но не данные. Ну и что? Для любого своего класса или структуры вы можете написать такой же оператор и они будут вести себя абсолютно так же.
Более того, иммутабельность строк можно нарушить с помощью небольшой щепотки магии. И да, это уже было на хабре.
const vs readonly
Заблуждение:
Закон:
12.20 Constant expressions
A constant expression is an expression that shall be fully evaluated at compile-time
Не «установить значение», а «значение вычисляется» (это тонкое, но важное различие).
15.5.3.1 General When a field-declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class.
Да, вроде бы похоже. Но почему они в разных разделах (12 и 15)? Давайте посмотрим на название: одно — это выражение, а другое — модификатор поля. И это главное их семантическое отличие.
Отсюда вытекает важная деталь реализации:
15.5.3.3 Versioning of constants and static readonly fields
Constants and readonly fields have different binary versioning semantics. When an expression references a constant, the value of the constant is obtained at compile-time, but when an expression references a readonly field, the value of the field is not obtained until run-time.
Значение константы фиксируется на момент компиляции. А статических readonly-полей (которые часто используют как замену) — на этапе выполнения. Если одна сборка зависит от другой, и берёт из неё константы и ридонли-поля, то при их изменении в первой сборке, константы во второй останутся старыми, а readonly-поля подцепятся свежие.
Факт: соберите тестовый проект, и откройте его в декомпиляторе. Вы увидите, что вместо констант встанут их значения (поэтому они и являются выражениями), а для readonly-полей останутся на них ссылки.
ref и out
Из статьи-«шпаргалки»: ref и out позволяют внутри метода использовать new и для class и для struct
Факт: см выше (Передача по указателю)
out тоже что ref, только говорит о том что, метод обязательно пересоздаст переменную
Тут, наверное, имелось в виду «переназначит», а не пересоздаст, но сильно придираться не будем.
События, делегаты
Заблуждение:
Закон: 15.8.2 Field-like events
Надеюсь, разницу, объяснять не надо.
P.S.: в новом C# можно не задумываться и писать Evt?.Invoke(«hello»);
Finalizer (destructor)
Заблуждение (1):
Foo() это «деструктор» класса Foo
Почему? Потому что по той или иной причине сами авторы языка так это называли, хотя вовремя одумались.
Закон:
15.13 Finalizers
[Note: In an earlier version of this standard, what is now referred to as a «finalizer» was called a
«destructor». Experience has shown that the term «destructor» caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use Dispose. end note]
Причина: деструкторы и финализаторы отличаются семантикой, главное отличие — детерминированность.
P.S.: в комментах появилась хорошая историческая справка, спасибо @rstm-sf.
Заблуждение (2): вызывается, когда garbage collector доберется до объекта
Почему популярно? В большинстве простых случаев это действительно именно так и происходит.
Закон:
An instance becomes eligible for finalization when it is no longer possible for any code to use that instance. Execution of the finalizer for the instance may occur at any time after the instance becomes eligible for finalization (§8.9).
Обращаем внимание, что никаких гарантий нам тут не предоставляют.
Факт: финализатор будет вызван у объекта, если он у него есть и объект не побывал в методе SuppressFinalize до начала маркировки объектов на финализацию. Где-то между моментом определения что объект недоступен до момента фактического освобождения памяти. Но это не точно. Отменить финализацию можно с помощью метода GC.SuppressFinalize, хотя это может и не сработать. Более того, рекомендованный шаблон реализации IDisposable именно так и поступает.
Факт: действительно, специального синтаксиса для этого нет. Но можно, как обычно, залезть туда через рефлекшен:
Конечно, для симуляции корректного поведения следует пройтись по всей цепочке наследования (ведь так написано в стандарте).
Singleton
Заблуждение: не забудьте про потокобезопасность и lock
Почему? Потому что часто синглтон смешивают с ленивой инициализацией, хотя это два разных паттерна, которые могут встретиться независимо друг от друга.
В стандарте очевидно ничего такого нет, так что будем опираться на здравый смысл.
Раньше собеседующий ждал тут что-то типа такого кода:
Это известный паттерн double checked locking, он нужен для ленивой инициализации. А вопрос, напомню, стоял про синглтон.
Более того, этот код тоже имеет проблемы. Дело в том, что модель памяти работает не совсем так, как кажется на первый взгляд (это отдельная большая тема, явно не для собеседований уровня джун/миддл). Чтобы её решить, требуется или вставить volatile в нужном месте или аккуратно использовать MemoryBarrier().
Опыт: ничего из этого на самом деле не требуется для синглтона. Ведь рантайм нам даёт чёткие гарантии о потокобезопасности статических инициализаторов и мы просто можем написать:
Рантайм гарантирует, что это свойство будет потокобезопасно проинициализировано в какой-то момент начиная от запуска и до первого использования. Причём в текущей реализации это делается с достаточной степенью ленивости, так что для 99% случаев такой простой код будет самым безопасным и надёжным. Для любителей чуть большей ленивости есть решение со вложенным классом, но всё же не полной гарантией ленивости.
И, значит, для оставшегося 1% случаев, когда синглтону нужна гарантированная ленивость, мы можем написать:
И да, это тоже уже было на хабре, с кучей комментов.
P.S.: и так совпало, что сегодня же на хабре выложили вот такую подробную статью (18+)!
Вроде, по статье всё. Я явно что-то забыл, или написал неправильно, но для этого есть комменты, да будет срач!