Требование к типу литерала в функциях 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 должно удовлетворять следующим требованиям: ...

  • каждый из его типов параметров должен быть буквальным типом ; ...

И базовый #types.general-10:

Тип является буквальным типом, если он: ...

  • это либо тип закрытия, либо агрегатный тип , либо ...

Я понимаю причину почему 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{};
};
Другие вопросы по тегам