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?