Patch for LWG issue #2145

David Blaikie dblaikie at gmail.com
Wed Aug 21 15:53:21 PDT 2013


On Tue, Aug 20, 2013 at 8:38 PM, Howard Hinnant <hhinnant at apple.com> wrote:
>
> On Aug 20, 2013, at 11:12 PM, David Blaikie <dblaikie at gmail.com> wrote:
>
>> On Tue, Aug 20, 2013 at 11:11 AM, Marshall Clow <mclow.lists at gmail.com> wrote:
>>> http://cplusplus.github.io/LWG/lwg-defects.html#2145
>>>
>>> Mark the constructor for std::error_category as inline and constexpr.
>>> Leave the (existing, out-of-line, non-constexpr) in the dylib for compatibility with existing programs)
>>> No tests, because I don't know how to test it (given that error_category is both an abstract class and has a user-defined destructor)
>>
>> Curious - I haven't dealt with constexpr much, but could you explain
>> further why this is untestable yet is a useful/meaningful change to
>> make?
>
> Sure, fair question.
>
> It is legal to mark a constructor for a class as constexpr, even if that class can never be constructed as a constexpr variable:
>
> struct X
> {
>      constexpr X() {}
>      ~X();  //  disallow constexpr X here
> };
>
> One can legally:
>
> X x;
>
> but not:
>
> constexpr X x;
>
> So why mark X() constexpr?
>
> <disclaimer>I'm learning this in real-time at nearly the same time you are.</disclaimer>

Nice to know I'm not the only one.

> LWG 2145 discusses this a bit: http://cplusplus.github.io/LWG/lwg-defects.html#2145
>
> Apparently if an X is constructed with static scope, if the constructor is marked as constexpr, then it will be constructed at compile time, even if the variable itself is not marked constexpr.

Right - this would be my understanding though differs subtly from
Marshall's claim, which is that this isn't guaranteed. I assume it is
actually guaranteed or it'd be hard to actually write correct code
depending on this feature - it'd be a pure implementation detail
optimization.

> This has been used, for example, in the design of std::mutex:
>
>    std::mutex m;
>
> It is important that a global std::mutex be compile-time constructible so as to avoid race conditions during initialization code prior to main().  However you can't make mutexes constexpr:
>
>    constexpr std::mutex m;  // worthless
>
> because you have to mutate mutexes during program execution (lock them and unlock them).
>
> Testing that X or std::mutex is actually constructed at compile time, and not during initialization prior to main() is challenging.  I'm nearly sure it is possible.  However I haven't yet succeeded in writing such a test, much less, installing such a test in the libc++ test suite.  My current best guess is that such a test would involve at least two translation units, which the current libc++ test suite is not capable of handling.

I don't think it's quite that tricky, but I could be missing something.

Here's a test case that I intended as a runtime test:

#include <cassert>
struct base {
  base() {}
  virtual ~base() = 0;
};

base::~base() {
}

struct derived: base {
  int i;
  derived() : i(3) {
  }
};

extern derived d;

int func() {
  assert(d.i == 3);
  return 1;
}

int x = func();

derived d;

int main() {
}

This fails the assertion when derived/base's default ctors are not
constexpr (OK, so a sufficiently advanced compiler could inline the
initialization, I assume? I think global initializers are guaranteed
to occur in the order of declaration in the TU, so I'm not sure it's
actually allowed to do that - I think func() /must/ see 'd.i' as zero
first (or maybe it's UB to access the not-yet-constructed object?))
and passes when they are constexpr.

But in constructing this test case I stumbled across an even easier
way to test, at least with Clang (I'm not sure this is required to
fail to compile, though) as a compile-only test.

Declare derived's ctor constexpr - Clang will produce a compilation
error ("error: constexpr constructor never produces a constant
expression") if the base ctor is not constexpr. This would test the
change that Marshall made.

> As I'm writing this, Marshall has also responded.  I agree with what Marshall is saying, and add my own words as well.
>
> If anyone knows of a good way to write a test for the libc++ test suite that would test the presence/absence of a constexpr constructor for a non-literal type, that of course would be a welcome patch.  In the meantime, we stumble on. :-)
>
> Howard
>




More information about the cfe-commits mailing list