2

I have something similar to the following

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() { };
};

I would like to achieve some constexpr null constructor for the derived class, but the compiler complains a lot about the two options and similar tests I have done.

Of course the code is more complex, I have an opaque handler and the destructor should behave in a more complex way. The resource free-ing is always the same (no need for multiple destructors, only the Base one).

I don't know how to achieve this, maybe I am going through a wrong path? Any ideas? I expect to be able to do something like:

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

and, in cleanup, both handler1 and handler2 are managed in the some way.

Edit:

Clang (version 3.4) complains:

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

And gcc (version 4.8 [edit: multiple versions, haven't checked all]) doesn't complain when using

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

In fact, gcc seems to do what I wanted to achieve, but I do not understand the constexpr enough to know which compiler is doing right and how I can amend the problem.

MariusSiuram
  • 3,380
  • 1
  • 21
  • 40
  • 1
    The error of gcc I got (when using `constexpr Derived a(nullptr);`) is self explanatory: `'Derived' is not literal because:'Derived' has a non-trivial destructor`. (That already happens with `Base`). – Jarod42 Nov 27 '14 at 10:23
  • @Jarod42 Interesting, with gcc 4.8.1 -std=c++11 I don't get this error. – Martin Prazak Nov 27 '14 at 10:26
  • @Jarod42 but I assume that `unique_ptr` doesn't have a trivial destructor, and uses something similar to what I'm doing, doesn't it? – MariusSiuram Nov 27 '14 at 10:27
  • @Jarod42 @martin_pr `g++-4.7` version 4.7.3 compiles and runs as I wanted (`g++-4.7 (Ubuntu/Linaro 4.7.3-12ubuntu1) 4.7.3`) – MariusSiuram Nov 27 '14 at 10:33
  • I am still not sure what is the cause, but a newer version of clang gives a bit more details about the error: `note: non-literal type 'Base' cannot be used in a constant expression constexpr Derived(std::nullptr_t) : Base(nullptr) { }; ` – Martin Prazak Nov 27 '14 at 10:41
  • Note that I got also an error with `unique_ptr` (http://coliru.stacked-crooked.com/a/10f22fc762d1bcb4). – Jarod42 Nov 27 '14 at 10:43
  • `constexpr` changed in C++14, see [N3652](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html). With `-std=c++1y`, the warning goes away. – Kerrek SB Nov 27 '14 at 11:09

1 Answers1

5

The type of a constant expression must be a literal type. In fact, the entire purpose of the "literal type" taxon is to "be the thing that can be a constant expression". See [expr.const]:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

...

— an invocation of a function other than a constexpr constructor for a literal class, a constexpr function,

...

Therefore, a constexpr constructor only allows you to produce constant expressions on literal classes, and otherwise, as your compiler is telling you, it would "never produce a constant expression".

A literal class is constrained by [basic.types] in this way:

A type is a literal type if it is:

...

— a class type (Clause 9) that has all of the following properties:

  • it has a trivial destructor,
  • it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
  • all of its non-static data members and base classes are of non-volatile literal types.

However, as of C++14 (particularly, as of N3652), constexpr constructors have another, unrelated use: They permit static initialization (in the sense of [basic.start.init]):

A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types [Note: such a class may have a non-trivial destructor — end note].

So to recap: As of C++14, constexpr has two uses:

  1. The C++11 interpretation: a "constant expression" is an expression that is identical to its value (i.e. its evaluation has no side effects); constexpr variables are just placeholders for their value and not intended to be used for their object identity, and it is generally expected that constant expressions can be freely replaced with their (compile-time knowable) value.

  2. The C++14 constexpr functions, including constructors: These functions, including constructors, may be called in the static initialization phase to constant-initialize variables with static storage duration. If the variables are objects, they still retain their object identity and may need dynamic destruction, but their initialization happens before any dynamic initialization and is not subject to ordering.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084