Требование к типу литерала в функциях constexpr
Вот мой код:
class agg_t1{
int x; // private non-static data menber
};
class agg_t2{
agg_t2(){} // user-provided constructor
};
constexpr void ce1(agg_t1 arg){}; // OK
constexpr void ce2(agg_t2 arg){}; // ERROR: parameter type 'agg_t2' is not a literal type
Согласно dcl.constexpr:
Определение функции constexpr должно удовлетворять следующим требованиям: ...
- каждый из его типов параметров должен быть буквальным типом ; ...
Тип является буквальным типом, если он: ...
- это либо тип закрытия, либо агрегатный тип , либо ...
Я понимаю причину почему
agg_t2
не является буквальным типом, он нарушает правило dcl.init.aggr# 1.1 :
Агрегат - это массив или класс с ...
- нет объявленных пользователем или унаследованных конструкторов ...
и я думаю, что это может быть не буквальный тип, потому что он также нарушает правило dcl.init.aggr# 1.1 :
Агрегат - это массив или класс с ...
- нет частных или защищенных прямых нестатических членов данных ...
Однако ... результат компилятора говорит мне, что я ошибался в предположении для.
У меня вопрос:
Член личных данных If
x
делает его неагрегатным типом, тогда почему
agg_t1
тип разрешен в
constexpr
определение функции
ce1
?
1 ответ
Член закрытых данных If
x
делает его неагрегатным типом, то почему этот тип разрешен в определении функцииce1
?
С++ 20
действительно не является агрегатным классом из-за его закрытого члена данных, однако, хотя агрегатность может быть одним из достаточных требований для того, чтобы тип класса был буквальным типом, это не обязательно . Обратите внимание на условие or [basic.types.general]/10.5.2 [ курсив мой]:
Тип является буквальным типом, если он:
- [...]
- возможно тип класса cv-qualified , который имеет все следующие свойства :
- [...]
- это либо тип замыкания ([expr.prim.lambda.closure]), либо агрегатный тип ([dcl.init.aggr]), либо он имеет по крайней мере один конструктор constexpr или шаблон конструктора (возможно, унаследованный ([namespace.udecl] ) из базового класса), который не является конструктором копирования или перемещения, [...]
Согласно [class.default.ctor]4 :
[...] Если этот написанный пользователем конструктор по умолчанию будет удовлетворять требованиям конструктора constexpr ([dcl.constexpr]), неявно определенный конструктор по умолчанию будет constexpr [...]
и [dcl.constexpr]/3 и [dcl.constexpr]/4 :
/3 Определение функции constexpr должно удовлетворять следующим требованиям:
- [...]
- если функция является конструктором или деструктором, ее класс не должен иметь виртуальных базовых классов; [...]
/4 Определение конструктора constexpr, тело функции которого не = delete, должно дополнительно удовлетворять следующим требованиям:
- для неделегирующего конструктора каждый конструктор, выбранный для инициализации нестатических элементов данных и подобъектов базового класса , должен быть конструктором constexpr ;
- [...]
неявно определенный конструктор по умолчанию для is , и, таким образом, [basic.types.general]/10.5.2 не лишает права быть буквальным типом в C++20.
С++ 17
В C++17 неявно определенный конструктор по умолчанию для
agg_t1
не является
constexpr
, так как это нарушает [dcl.constexpr]/4.5:
Определение конструктора constexpr должно удовлетворять следующим ограничениям:
- [...]
Кроме того, либо его тело-функция должно быть = delete, либо оно должно удовлетворять следующим ограничениям:
- [...]
- /4.5 каждый невариантный нестатический элемент данных и подобъект базового класса должен быть инициализирован ([class.base.init]);
И действительно, в то время как Clang и GCC отвергают следующее из-за
-std=c++17
:
class A {
constexpr A() = default; // error: cannot be constexpr
private:
int x;
};
принято следующее:
// OK: B is a literal type.
class B {
constexpr B() = default; // OK
private:
int x{};
};