Глядя на определение тривиального конструктора по умолчанию в стандартах:
Конструктор по умолчанию является тривиальным, если он не предоставляется пользователем, и если:
- его класс не имеет виртуальных функций (10.3) и нет виртуальных базовых классов (10.1) и
- нет нестатического элемента данных его класса, который имеет инициализатор скобок или равный, и
- все прямые базовые классы его класса имеют тривиальные конструкторы по умолчанию, а
- для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый из таких классов имеет тривиальное значение по умолчанию конструктор.
В противном случае конструктор по умолчанию является нетривиальным.
Похоже, что определение тривиальности конструктора по умолчанию не исключает возможности конструктора deleted
по умолчанию:
struct A {
int& a; // the implicitly defaulted default constructor will be defined as deleted
};
struct B {
B()=delete; // explicitly deleted
};
int main() {
static_assert(is_trivial<A>::value, "");
static_assert(is_trivial<B>::value, "");
}
Вышеприведенный код работает без какого-либо отказа утверждения. Тип имеет тривиальный конструктор по умолчанию и тривиально копируется, поэтому он "trivial class"
.
Не сделал бы такой тип "trivial class"
проблемой? Например, для таких вещей, как время жизни объекта, эквивалентность копии по байтам, ведомость инструкции goto
и т.д.
РЕДАКТИРОВАТЬ: Следующий пример подтверждения goto недействителен. Спасибо за комментарий @Casey. Еще один пример байт-мудральной эквивалентности копии добавлен для замены этого.
В качестве примера возьмем выражение goto
, стандарты говорят:
Можно передать в блок, но не таким образом, чтобы обходит объявления с инициализацией. Программа, которая прыгает87 из точка, где переменная с автоматической продолжительностью хранения не находится в сфера до точки, в которой она находится в сфере охвата, переменная имеет скалярный тип, тип класса с тривиальным значением по умолчанию конструктор и тривиальный деструктор, cv-квалифицированная версия одного из эти типы или массив одного из предыдущих типов и объявляется без инициализатора (8.5).
Итак, для следующего кода:
class A {
int& a;
public:
A(int& aa): a{aa} {}
A()=default; // this is necessary otherwise no default constructor will be implicitly declared then the type will not be trivial
};
int i;
int main() {
static_assert(is_trivial<A>::value, "");
goto L;
A a{i};
L:
return 0;
}
Он корректно сформирован в соответствии с правилами, потому что A
имеет тривиальный конструктор по умолчанию и тривиальный деструктор (утверждение проходит ОК). Напротив, код плохо сформирован в С++ 03 (при синтаксисе С++ 11-only удален, т.е. в строке A()=default;
), потому что A
не является POD
в С++ 03, а С++ 03 позволяет goto
пересекать только определение типа POD
.
В качестве примера возьмем байт-мудрую эквивалентность копии, в стандарте говорится:
Для любого тривиально-скопируемого типа T, если два указателя на T указывают на различные T-объекты obj1 и obj2, где ни obj1, ни obj2 не являются subobject базового класса, если базовые байты (1.7), составляющие obj1, являются скопированные в obj2,41 obj2, впоследствии должны иметь такое же значение, как и obj1.
So memcpy()
для тривиально-скопируемого типа хорошо определен:
class A {
int& a;
public:
A(int& aa): a{aa} {}
A()=default; // this is necessary otherwise no default constructor will be implicitly declared then the type will not be trivial
void* addr() {return &a;}
};
int i = 0;
int j = 0;
int main() {
static_assert(is_trivial<A>::value, "");
A a{i};
A b{j};
cout << a.addr() << " " << b.addr() << "\n";
// a = b; // this will be ill-formed because the implicitly defaulted copy assignment is defined as deleted
memcpy(&a, &b, sizeof(A)); // this is well-defined because A is trivial
cout << a.addr() << " " << b.addr() << "\n";
}
Он корректно определен в соответствии с правилами, потому что A
является тривиальным типом (утверждение проходит ОК). Результат показывает, что делается ссылка на разные объекты в разное время. Наоборот, код undefined в С++ 03 (с синтаксисом С++ 11-only удален, т.е. строка A()=default;
), потому что A
не является POD
в С++ 03, а С++ 03 допускает только эквивалентную копию копии типа POD
.