Информация об изменениях

Сообщение Re[47]: Зачем плюс? от 02.12.2018 6:58

Изменено 02.12.2018 10:25 N. I.

Re[47]: Зачем плюс?
σ:

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

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 должно работать де-факто, формально гарантия идентичности объектов нигде явным образом не выражена.
Re[47]: Зачем плюс?
σ:

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

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 должно работать де-факто, формально гарантия идентичности объектов нигде явным образом не выражена.