constexpr用于派生类中的null-initialized构造函数

时间:2014-11-27 10:10:12

标签: c++ c++11

我有类似以下的内容

class Base {
public:
    explicit Base(int* i) noexcept { type = new int; *type = *i; };
    constexpr Base(std::nullptr_t) : type(nullptr) { };
    ~Base() { cout << "Destroying!" << endl; delete type; };
protected:
    int* type;
};


class Derived : public Base {
public:
    explicit Derived(int* i) noexcept : Base(i) { };
    //constexpr Derived(std::nullptr_t) : type(nullptr) { };
    //constexpr Derived(std::nullptr_t) : Base(nullptr) { };
    ~Derived() { };
};

我想为派生类实现一些constexpr null构造函数,但编译器对我所做的两个选项和类似测试抱怨很多。

当然代码更复杂,我有一个不透明的处理程序,析构函数应该以更复杂的方式运行。资源自由总是相同的(不需要多个析构函数,只需要Base个)。

我不知道如何实现这一点,也许我正走错路?有任何想法吗?我希望能够做到这样的事情:

Derived a(nullptr);
Derived b(handler1);
Base c (nullptr);
Base d (handler2);

并且,在清理方面,handler1handler2都以某种方式进行管理。

编辑:

Clang(3.4版)抱怨:

error: constexpr constructor never produces a constant expression [-Winvalid-constexpr]

gcc(版本4.8 [编辑:多个版本,尚未全部检查])在使用时没有抱怨

constexpr Derived(std::nullptr_t) : Base(nullptr) { };

事实上,gcc似乎做了我想要实现的目标,但我不明白constexpr足以知道哪个编译器正在做正确以及如何修改问题。

1 个答案:

答案 0 :(得分:5)

常量表达式的类型必须是文字类型。事实上,“文字型”分类的整个目的是“成为一个可以不断表达的东西”。见[expr.const]:

  

条件表达式 e是一个核心常量表达式,除非根据抽象机器(1.9)的规则评估e将评估其中一个以下表达式:

     

...

     

- 调用一个文字类的constexpr构造函数以外的函数,一个constexpr函数,

     

...

因此,constexpr构造函数只允许您在文字类上生成常量表达式,否则,正如您的编译器告诉您的那样,它将“永远不会产生常量表达式”。

文字类以这种方式受[basic.types]约束:

  

类型是文字类型,如果它是:

     

...

     

- 具有以下所有属性的类类型(第9节):

     
      
  • 它有一个简单的析构函数,
  •   
  • 它是聚合类型(8.5.1)或至少有一个constexpr构造函数或构造函数模板,它不是复制或移动构造函数,并且
  •   
  • 它的所有非静态数据成员和基类都是非易失性文字类型。
  •   

但是,从C ++ 14开始(特别是N3652),constexpr构造函数有另一个不相关的用法:它们允许静态初始化(在[basic.start.init]意义上]):

  

对象o常量初始值设定项是一个表达式   常量表达式,除了它还可以为constexpr及其子对象调用o构造函数,即使这些对象是非文字类类型[注意:这样的类可能有一个非平凡的析构函数 - 结尾注释]。

回顾一下:从C ++ 14开始,constexpr有两个用途:

  1. C ++ 11解释:“常量表达式”是与其值相同的表达式(即其评估没有副作用); constexpr变量只是它们的值的占位符,并不打算用于它们的对象标识,并且通常期望常量表达式可以用它们的(编译时可知的)值自由替换。

    < / LI>
  2. C ++ 14 constexpr函数,包括构造函数:这些函数(包括构造函数)可以在静态初始化阶段调用,以使用静态存储持续时间对变量进行常量初始化。如果变量是对象,它们仍然保留其对象标识并且可能需要动态销毁,但它们的初始化在任何动态初始化之前发生,并且不需要排序。