Re[33]: Зачем плюс?
От: Vain Россия google.ru
Дата: 02.12.18 00:52
Оценка:
Здравствуйте, rg45, Вы писали:

V>>Ты главное продолжай отвечать, я ведь лично с тобой общаюсь.

R>Как только мне понадобится твой совет, ты об этом сразу узнаешь.
И ещё раз.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[34]: Зачем плюс?
От: rg45 СССР  
Дата: 02.12.18 00:53
Оценка:
Здравствуйте, Vain, Вы писали:

V>И ещё раз.


Хоть два.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[47]: Зачем плюс?
От: N. I.  
Дата: 02.12.18 06:58
Оценка:
σ:

σ>>>Так что это мёртвые правила.

NI>>Они не мёртвые, а underspecified
σ>Это не одно и то же?

Underspecified правила применяются совместно с негласными правилами, мёртвые не применяются вовсе.

В качестве ещё одного примера underspecified правил можно взять пресловутое guaranteed copy elision, в основе которого лежит довольно мутная формулировка касаемо выполнения copy-initialization:

"If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object",

где ничего не говорится о том, как именно "the initializer expression is used to initialize the destination object". Нигде явно не сказано, что the result object of the initializer expression и the destination object of the copy initialization — это один и тот же объект. В большинстве случаев неявно подразумевается, что это действительно один и тот же объект, но в стандарте также имеется явная оговорка, допускающая иной сценарий выполнения copy-initialization:

When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function’s parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). [ Note: This latitude is granted to allow objects of class type to be passed to or returned from functions in registers. —end note ]

Так что, например, тут

#include <cstring>
#include <iostream>

struct X
{
    X(char const *s = "<empty>") : p(data) { std::strncpy(p, s, 7); }
    char *p;
    char data[8] = {};
};

X f()
{
    return X();
}

int main()
{
    X x = f();
    std::cout << x.p << std::endl;
}

guaranteed copy elision не применяется и указатель x.p вполне может оказаться невалидным (т.к. исходный объект уже мёртв) со всеми вытекающими.

В случаях же, когда guaranteed copy elision должно работать де-факто, формально гарантия идентичности объектов нигде явным образом не выражена.
Отредактировано 02.12.2018 10:25 N. I. . Предыдущая версия . Еще …
Отредактировано 02.12.2018 7:03 N. I. . Предыдущая версия .
Re[47]: Зачем плюс?
От: N. I.  
Дата: 02.12.18 06:59
Оценка:
rg45:

NI>>В последнее время плюсами я пользуюсь довольно редко и интерес к ним у меня уже совсем нет тот, что раньше.


R>С чем это связано? Если не секрет.


Надоели
Re[48]: Зачем плюс?
От: rg45 СССР  
Дата: 02.12.18 08:20
Оценка:
Здравствуйте, N. I., Вы писали:

NI>>>В последнее время плюсами я пользуюсь довольно редко и интерес к ним у меня уже совсем нет тот, что раньше.

R>>С чем это связано? Если не секрет.
NI>Надоели

Просто надоели, или, может, появились более интересные альтернативы?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[48]: Зачем плюс?
От: σ  
Дата: 02.12.18 17:09
Оценка:
NI>В качестве ещё одного примера underspecified правил можно взять пресловутое guaranteed copy elision, в основе которого лежит довольно мутная формулировка касаемо выполнения copy-initialization:
В основе guaranteed copy elision лежит несколько разных формулировок.

NI>"If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object",

NI>где ничего не говорится о том, как именно "the initializer expression is used to initialize the destination object".
Потому что стандарт — не учебник и не руководство по написанию компиляторов.

NI>Нигде явно не сказано, что the result object of the initializer expression и the destination object of the copy initialization — это один и тот же объект. В большинстве случаев неявно подразумевается

По-моему, это явно следует из определения prvalue и prvalue result (object). Материализация временного объекта означала бы, что не "the initializer expression is used to initialize the destination object", а the xvalue expression, полученный при temporary materialization conversion из the initializer expression, is used to initialize the destination object.

NI>в стандарте также имеется явная оговорка, допускающая иной сценарий выполнения copy-initialization:

NI>When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function’s parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). [ Note: This latitude is granted to allow objects of class type to be passed to or returned from functions in registers. —end note ]
Это не разрешение создавать временные объекты, это разрешение создавать временные объекты тогда, когда по правилам языка это может быть невозможно (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). Ну и примечание поясняет назначение этого параграфа.

NI>Так что, например, тут


NI>
#include <cstring>
NI>#include <iostream>

NI>struct X
NI>{
NI>    X(char const *s = "<empty>") : p(data) { std::strncpy(p, s, 7); }
NI>    char *p;
NI>    char data[8] = {};
NI>};

NI>X f()
NI>{
NI>    return X();
NI>}

NI>int main()
NI>{
NI>    X x = f();
NI>    std::cout << x.p << std::endl;
NI>}

NI>guaranteed copy elision не применяется

Почему? Для guaranteed copy elision подправили определение return statement. Там сказано: "the return statement initializes the glvalue result or prvalue result object of the (explicit or implicit) function call by copy-initialization from the operand."
Так что
X x = f();
эквивалентно
X x = X();
что в свою очередь означает инициализацию объекта x дефолтным конструктором.
Re[49]: Зачем плюс?
От: N. I.  
Дата: 02.12.18 19:28
Оценка:
σ:

NI>>Нигде явно не сказано, что the result object of the initializer expression и the destination object of the copy initialization — это один и тот же объект. В большинстве случаев неявно подразумевается

σ>По-моему, это явно следует из определения prvalue и prvalue result (object). Материализация временного объекта означала бы, что не "the initializer expression is used to initialize the destination object", а the xvalue expression, полученный при temporary materialization conversion из the initializer expression, is used to initialize the destination object.

Понятие "is used to" для данного контекста никак не формализовано, а в бытовом значении "использование" может быть как прямым, так и косвенным. Создание и использование объекта-посредника никак не противоречит косвенному использованию исходного initializer expression для инициализации конечного.

σ>Это не разрешение создавать временные объекты, это разрешение создавать временные объекты тогда, когда по правилам языка это может быть невозможно (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object). Ну и примечание поясняет назначение этого параграфа.


Если почитать то примечание внимательно, то становится ясно, что ключевым моментом здесь как раз является возможность создавать временный объект в неожиданном месте, а "even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object" — всего лишь небольшое уточнение.

σ>Так что

σ>X x = f();
σ>эквивалентно
σ>X x = X();
σ>что в свою очередь означает инициализацию объекта x дефолтным конструктором.

Разработчики GCC и Clang с тобой не согласны.
Re[50]: Зачем плюс?
От: σ  
Дата: 02.12.18 20:10
Оценка:
σ>>Так что
σ>>X x = f();
σ>>эквивалентно
σ>>X x = X();
σ>>что в свою очередь означает инициализацию объекта x дефолтным конструктором.

NI>Разработчики GCC и Clang с тобой не согласны.


Можно цитаты?
Re[51]: Зачем плюс?
От: N. I.  
Дата: 03.12.18 10:10
Оценка:
σ:

σ>Можно цитаты?


Цитаты чего? Я полагал, что ты догадаешься проверить, что реально делают компиляторы при дефолтных настройках оптимизации на каком-нибудь сайте типа godbolt.org.

https://gcc.godbolt.org/z/D24Xl2
https://gcc.godbolt.org/z/Bpc68R

В первом случае условие "if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor" выполняется и оба компилятора создают внутри f отдельный объект, после чего копируют его p и data через регистры rax и rdx, а втором данное условие не выполняется и оба компилятора передают адрес переменной x в f через регистр rdi и дальше этот же адрес передаётся в конструктор.
Re[7]: Зачем плюс? Низачем.
От: alexku Россия  
Дата: 03.12.18 10:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

Вот здесь всё хорошо описано.
Re[52]: Зачем плюс?
От: σ  
Дата: 03.12.18 15:37
Оценка:
σ>>Можно цитаты?
NI>Цитаты чего?
Разработчиков.

NI>Я полагал, что ты догадаешься проверить, что реально делают компиляторы при дефолтных настройках оптимизации на каком-нибудь сайте типа godbolt.org.

Я проверил ещё при ответе на первое сообщение с этим кодом.

Есть багрепорты с ответами от разработчиков, что это ОК? Или тут просто неполное соответствие C++17?
Re[53]: Зачем плюс?
От: N. I.  
Дата: 03.12.18 19:11
Оценка: 1 (1)
σ:

σ>>>Можно цитаты?

NI>>Цитаты чего?
σ>Разработчиков.

Примерно полтора года назад данные правила вместе с примером кода разбирались на рефлекторе комитета по стандартизации, разъяснения давал автор этих правил — Ричард Смит (обсуждение закрытое, так что ссылку дать не смогу).

N.I.: The wording "the initializer expression is used to initialize the destination object" seems to say nothing certain about _how_ exactly the expression is used to initialize the destination object. So, what is it supposed to mean? Should it mean that if the initializer expression denotes some object, then the destination object must be the same object as the one denoted by the initializer expression?

R.S.: If the initializer expression is a prvalue, there is no other object; you're using two different terms in your question to refer to objects that are the same by definition. Please consult the definition of prvalue in [basic.lval]/1.2. Note in particular that a prvalue does not itself create an object; the object is provided by the context in which the prvalue is evaluated. [Из этого коммента я делаю вывод, что чел либо не понимает разницу между формальными и неформальными описаниями, либо считает, что строгая формализация в данном случае не требуется, ибо "и так всё понятно"].

N.I.:
***
When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function’s parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).
***

which implies that the source object and the destination object may be two distinct objects allocated at different addresses.

R.S: That's correct; [class.temporary]/3 specifies an (optional) exception to the general rule. Note that it explicitly says that the implementation creates an additional temporary object in this case.

N.I.: This can be observed in a program like this:

<дальше приводится такая же программа, что я тут продемонстрировал выше>

If the initially created temporary is copied, the field p of a copy does not point to a valid array object (data) and an attempt to access the value of x.p results in undefined behavior.

R.S.: Right. This is a design error in class X. [Тут он, видимо, имел в виду, что перемещающий конструктор X следовало определить должным образом]

σ>Есть багрепорты с ответами от разработчиков


Не знаю, мне такие не попадались. Но я точно помню, что gcc 8.0 ошибочно делал копирование результата через регистры даже в том случае, если в X добавить deleted move constructor, не внося какие-либо другие изменения. В версии 8.1 этот баг уже пофиксен.
Re[54]: Зачем плюс?
От: σ  
Дата: 03.12.18 19:59
Оценка:
σ>>>>Можно цитаты?
NI>>>Цитаты чего?
σ>>Разработчиков.

NI>Примерно полтора года назад данные правила вместе с примером кода разбирались на рефлекторе комитета по стандартизации, разъяснения давал автор этих правил — Ричард Смит (обсуждение закрытое, так что ссылку дать не смогу).


У меня есть доступ в рид-онли.

NI>N.I.: The wording "the initializer expression is used to initialize the destination object" seems to say nothing certain about _how_ exactly the expression is used to initialize the destination object. So, what is it supposed to mean? Should it mean that if the initializer expression denotes some object, then the destination object must be the same object as the one denoted by the initializer expression?


NI>R.S.: If the initializer expression is a prvalue, there is no other object; you're using two different terms in your question to refer to objects that are the same by definition. Please consult the definition of prvalue in [basic.lval]/1.2. Note in particular that a prvalue does not itself create an object; the object is provided by the context in which the prvalue is evaluated. [Из этого коммента я делаю вывод, что чел либо не понимает разницу между формальными и неформальными описаниями, либо считает, что строгая формализация в данном случае не требуется, ибо "и так всё понятно"].


Ну вообще мне тоже кажется, что определения prvalue и его result (object) вполне достаточно для понимания правила про инициализацию.
А формализация? Можно всё, что не формально описанная операционная/денотационная семантика, назвать неформальным.

NI>N.I.:

NI>***
NI>When an object of class type X is passed to or returned from a function, if each copy constructor, move constructor, and destructor of X is either trivial or deleted, and X has at least one non-deleted copy or move constructor, implementations are permitted to create a temporary object to hold the function parameter or result object. The temporary object is constructed from the function argument or return value, respectively, and the function’s parameter or return object is initialized as if by using the non-deleted trivial constructor to copy the temporary (even if that constructor is inaccessible or would not be selected by overload resolution to perform a copy or move of the object).
NI>***

NI>which implies that the source object and the destination object may be two distinct objects allocated at different addresses.


NI>R.S: That's correct; [class.temporary]/3 specifies an (optional) exception to the general rule. Note that it explicitly says that the implementation creates an additional temporary object in this case.


NI>N.I.: This can be observed in a program like this:


NI><дальше приводится такая же программа, что я тут продемонстрировал выше>


NI>If the initially created temporary is copied, the field p of a copy does not point to a valid array object (data) and an attempt to access the value of x.p results in undefined behavior.


NI>R.S.: Right. This is a design error in class X. [Тут он, видимо, имел в виду, что перемещающий конструктор X следовало определить должным образом]


Ок, буду знать, что этот параграф оставлен намеренно.
Отредактировано 03.12.2018 20:04 σ . Предыдущая версия .
Re[20]: Зачем плюс?
От: Максим Рогожин Россия  
Дата: 23.12.18 15:34
Оценка:
Здравствуйте, rg45, Вы писали:

Согласно 6.9:

R>

R>Types describe objects (4.5), references (11.3.2), or functions (11.3.5).


Кстати, а вот тип void что из вышеперечисленного описывает? Объектов типа void нету, вроде как.
Re[25]: Зачем плюс?
От: Максим Рогожин Россия  
Дата: 23.12.18 16:24
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>
BFE>void foo(int&& n){}

BFE>int main()
BFE>{
BFE>    int n = 3;
BFE>    foo(n);     // мы точно знаем, что у n тип int. ошибка компиляции.
BFE>    foo(1 + n); // ошибки компиляции нет, значит результат вычисления (1 + n)  - не int.
BFE>}
BFE>

BFE>Я полагаю, что результат вычисления имеет тип int&&. Почему это не так?

Этот пример, мог бы быть аргументом если бы тип функции был БЕССЫЛОЧНЫМ. В случае бессылочных типов компилятор принимает решение подходит или нет аргумент на основании только лишь информации о типе. Но для ссылочных (lvalue, rvalue) типов еще ДОПОЛНИТЕЛЬНО учитывается информация о value category. Правильно?

Вообще похоже что система типов в С++ соостоит из трех очень разных категорий типов: объекты, ссылки, функции. Те аргументы которые верны для одной категории (объекты) не уже не будут верны для другой категории (ссылки). Вот это, как мне кажется и не учел, B0FEE664.
Re[21]: Зачем плюс?
От: rg45 СССР  
Дата: 23.12.18 18:16
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Кстати, а вот тип void что из вышеперечисленного описывает? Объектов типа void нету, вроде как.


Вот тут не уверен. Вот, что говорит стандарт:

6.9.1. Fundamental types
9 A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. Any expression can be explicitly converted to type cv void (8.4). An expression of type cv void shall be used only as an expression statement (9.2), as an operand of a comma expression (8.19), as a second or third operand of ?: (8.16), as the operand of typeid, noexcept, or decltype, as the expression in a return statement (9.6.3) for a function with the return type cv void, or as the operand of an explicit conversion to type cv void.


Но точно не ссылка и не функция. Либо он относится к объектным типам и имеет тот же статус, что и неполные типы объеков, с тем лищь отличием, что void не может стать полным в принципе. Либо вообще это отдельный тип. Но, исходя из того, что void не упомянут в 6.9, как специальный случай, выходит, что его следует относить к объектным типам, все-таки.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[22]: Зачем плюс?
От: rg45 СССР  
Дата: 23.12.18 18:26
Оценка:
R>Здравствуйте, Максим Рогожин, Вы писали:

МР>>Кстати, а вот тип void что из вышеперечисленного описывает? Объектов типа void нету, вроде как.


R>Вот тут не уверен. Вот, что говорит стандарт:


R>

R>6.9.1. Fundamental types
R>9 A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. Any expression can be explicitly converted to type cv void (8.4). An expression of type cv void shall be used only as an expression statement (9.2), as an operand of a comma expression (8.19), as a second or third operand of ?: (8.16), as the operand of typeid, noexcept, or decltype, as the expression in a return statement (9.6.3) for a function with the return type cv void, or as the operand of an explicit conversion to type cv void.


R>Но точно не ссылка и не функция. Либо он относится к объектным типам и имеет тот же статус, что и неполные типы объеков, с тем лищь отличием, что void не может стать полным в принципе. Либо вообще это отдельный тип. Но, исходя из того, что void не упомянут в 6.9, как специальный случай, выходит, что его следует относить к объектным типам, все-таки.


Еще такое соображение в пользу того, что void — объектный тип. Указатель на любой объект может быть приведен к cv void*, а указатель на функцию не может. Ну а на ссылку void вообще не похож, ни по каким критериям. Поэтому выходит, что только объектный.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 23.12.2018 18:27 rg45 . Предыдущая версия .
Re[23]: Зачем плюс?
От: σ  
Дата: 23.12.18 22:52
Оценка: 16 (1)
R>Еще такое соображение в пользу того, что void — объектный тип. Указатель на любой объект может быть приведен к cv void*, а указатель на функцию не может. Ну а на ссылку void вообще не похож, ни по каким критериям. Поэтому выходит, что только объектный.

https://timsong-cpp.github.io/cppwp/n3337/basic.types#def:object_type, https://timsong-cpp.github.io/cppwp/n4140/basic.types#def:object_type​: An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type.
https://timsong-cpp.github.io/cppwp/n4659/basic.types#def:object_type​: An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not cv void.
Отредактировано 23.12.2018 22:54 σ . Предыдущая версия .
Re[20]: Зачем плюс?
От: Максим Рогожин Россия  
Дата: 24.12.18 18:01
Оценка:
Здравствуйте, rg45, Вы писали:

R>Согласно 6.9:


R>

R>Types describe objects (4.5), references (11.3.2), or functions (11.3.5).


А вот кстати, что написано на cppreference:

Objects, references, functions including function template specializations, and expressions have a property called type


Т.е. Type describes objects, references, functions and expressions
Re[35]: Зачем плюс?
От: Максим Рогожин Россия  
Дата: 24.12.18 18:41
Оценка:
Здравствуйте, σ, Вы писали:

σ>Что такое "объект" стандарт тоже не определяет, например. Только говорит, как он создаётся.


А cppreference определяет)

An object, in C++, is a region of storage that has
— size (can be determined with sizeof);
— alignment requirement (can be determined with alignof);
— storage duration (automatic, static, dynamic, thread-local);
— lifetime (bounded by storage duration or temporary);
— type;
— value (which may be indeterminate, e.g. for default-initialized non-class types);
— optionally, a name.

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.