Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to cast this to `Derived*` in the base ctor-initialiser?

Given the following example of CRTP:

template <typename T>
int foo(T* const)
{
   return 0;
}

template <typename Derived>
struct Base
{
   Base() : bar(foo(static_cast<Derived*>(this)) {};
   int bar;
};

struct Derived1 : Base<Derived1> {};

Is the conversion of this to Derived* here valid? I seem to recall that it may not be, but can find no concrete evidence of this now.

The "natural" type of this at this stage is Base* const, and there are certainly some occasions in which even statically casting the this pointer during initialisation is not okay, such as upcasting before base construction has completed (12.7/3).

@DeadMG says:

there's an explicit exception in the Standard w.r.t. obtaining this in the initializer list. it's for passing pointers to yourself to subobjects.

12.6.2/12 does say:

[ Note: Because the mem-initializer are evaluated in the scope of the constructor, the this pointer can be used in the expression-list of a mem-initializer to refer to the object being initialized. —end note ]

... though this is not sufficient to say that the conversion to Derived* is valid.

My intuition is that, during this phase of the object's initialisation, this does not point to an instance of Derived and, as such, even just having a pointer to it with type Derived* is, strictly speaking, UB. That's because it's neither a valid pointer nor a null pointer.

(This potentially has practical ramifications for approaches like this, though in that answer and my example above the whole thing could be side-stepped by simply writing static_cast<Derived*>(0) instead.)

like image 637
Lightness Races in Orbit Avatar asked Feb 04 '13 13:02

Lightness Races in Orbit


1 Answers

I think that it is UB.

As Mike said:

At that point, storage has been allocated for the complete object, but only the base subobject has been initialised. Therefore, you can only use the rest of an object in the "limited ways" described in C++11.

and it's my interpretation that this is not one of those ways.

More formally:

[C++11: 3.8/1]: The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [ Note: initialization by a trivial copy/move constructor is non-trivial initialization. — end note ] The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and
  • if the object has non-trivial initialization, its initialization is complete.

and:

[C++11: 3.8/5]: Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise, such a pointer refers to allocated storage (3.7.4.2), and using the pointer as if the pointer were of type void*, is well-defined. Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if:

  • the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression,
  • the pointer is used to access a non-static data member or call a non-static member function of the object, or
  • the pointer is implicitly converted (4.10) to a pointer to a base class type, or
  • the pointer is used as the operand of a static_cast (5.2.9) (except when the conversion is to void*, or to void* and subsequently to char*, or unsigned char*), or
  • the pointer is used as the operand of a dynamic_cast [..]
like image 200
Lightness Races in Orbit Avatar answered Oct 23 '22 01:10

Lightness Races in Orbit