0

I am trying to better understand C++ class member visibility as it pertains to the CRTP (curiously recurring template pattern). I should note that I understand the use of CRTP and its "compile time polymorphism".

I have the following snippet of code.

// A C++ constraint for a type that is "closable".
template <typename T>
concept IsClosable = requires(T t)
{
  { t.closeImplementation() } -> std::same_as<void>;
};

// I defer the constraint "IsClosable" to the function rather than the class
// otherwise when I use CRTP, the constraint would fail. 
template <typename T>
class Base
{
public:
  template <IsClosable C = T>
  void close()
  {
    static_cast<C*>(this)->closeImplementation();
  }
};

class Derived : public Base<Derived>
{
// Visibility in question here.
public:
  void closeImplementation()
  {

  }
};

I have attempted to trace what the compiler is actually seeing here. For simplicity I will remove the constraint. Doing so I have the following

  1. Restating the base code
template <typename T>
class Base
{
public:
  void close()
  {
    static_cast<T*>(this)->closeImplementation();
  }
};

class Derived : public Base<Derived>
{
// Visibility in question here.
public:
  void closeImplementation()
  {

  }
};
  1. The compiler first ignores the template declaration as nothing instantiates it yet.

  2. The compiler then comes across this line and starts to instantiate a template. Note that even though the full Derived class is not declared, this should be sufficient for template substitution.

class Derived : public Base<Derived>

leading to the following

template <>
class Base<Derived>
{
public:
  void close()
  {
    static_cast<Derived*>(this)->closeImplementation();
  }
};

I am not fully sure if this line makes sense just yet until we have the inheritance. It seems like before the inheritance takes place we are casting this which is a Base<Derived> base object to Derived which doesn't seem compatible.

  1. After the template is instantiated we then continue the declaration of the
class Derived : public Base<Derived>
{
// Visibility in question here.
public:
  void closeImplementation()
  {

  }
};

And if we substitute the methods we get from Base<Derived> this should more or less look like

class Derived // Inheriting from Base<Derived>
{
public:
  void closeImplementation()
  {

  }

  void close()
  {
    static_cast<Derived*>(this)->closeImplementation();
  }
};

So from this I have a few questions.

  1. Why can closeImplementation not be private if it "looks" like "close" can still access it?

  2. Does the line static_cast<Derived*>(this)->closeImplementation() get invoked in the Base<Derived> class or Derived? If it is the former, how does that work?

  3. Is my understanding of how the compiler views this code correct?

9
  • 1
    When I do CRTP, in my Derived I do friend Base<Derived>;.
    – Eljay
    Commented Mar 23 at 21:59
  • Agree with @Elijay. Because this is "implementation", it should not be public. But you should make it possible to access by the Base. So "friend" fits very well here.
    – ivan.ukr
    Commented Mar 23 at 22:40
  • "Does the line static_cast<Derived*>(this)->closeImplementation() get invoked in the Base<Derived> class or Derived?" -- The line is inside Base<T>::close(). Why would it be invoked anywhere else? (Don't let yourself be confused by "more or less look like". You can rearrange code to get a better handle on the semantics of a class, but not to understand the syntax.)
    – JaMiT
    Commented Mar 23 at 22:57
  • "I am not fully sure if this line makes sense just yet until we have the inheritance." -- Your reasoning looks good (but imprecise about what is instantiated) up to this point. See class template instantiation -- instantiating the class does not imply instantiating the member functions. Is that close enough to be a duplicate?
    – JaMiT
    Commented Mar 23 at 23:15
  • @JaMiT That is correct that it is inside Base<T>::close but the surrounding closure is not exactly clear to me. Is it executing in the context of the Base class or the Derived class? What type is this here? Does it depend on it's closure?
    – nick2225
    Commented Mar 24 at 0:10

0

Browse other questions tagged or ask your own question.