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
- 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()
{
}
};
The compiler first ignores the template declaration as nothing instantiates it yet.
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.
- 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.
Why can
closeImplementation
not be private if it "looks" like "close" can still access it?Does the line
static_cast<Derived*>(this)->closeImplementation()
get invoked in theBase<Derived>
class orDerived
? If it is the former, how does that work?Is my understanding of how the compiler views this code correct?
Derived
I dofriend Base<Derived>;
.static_cast<Derived*>(this)->closeImplementation()
get invoked in theBase<Derived>
class orDerived
?" -- The line is insideBase<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.)Base<T>::close
but the surrounding closure is not exactly clear to me. Is it executing in the context of theBase
class or theDerived
class? What type isthis
here? Does it depend on it's closure?