私の元の答えが実際には手元にある最終的なオーバーライドの質問を扱っていないことに気付いた後、私はそれに追加しようと思いました。以前の回答と同様の方法で「最終的なオーバーライド」ソリューションを考え出したかったのです。
問題:
CRTP インターフェイス クラスは常に、静的キャストを介して最上位の派生クラスにリダイレクトします。これは、「最終」関数の概念と矛盾しています。目的の「最終」関数が最上位の派生クラスで実装されておらず、上位クラスによって「オーバーライド」されている場合 (関数に「最終」を与えることができないため) CRTPで回避しようとしている仮想でない限り、CRTPインターフェイスは目的の「最終」機能ではなく「オーバーライド」にリダイレクトします。
ソリューション:
インターフェイスを 3 つの概念に分割します。
- 以下を継承するリダイレクト関数を持たない抽象インターフェイス クラス:
- リダイレクト関数が最上位の派生クラスにリダイレクトされる抽象リダイレクトクラス。
- 具体的な「リダイレクト オーバーライド」クラス。リダイレクト関数を実装でオーバーライドします。
具象実装クラスをインスタンス化するとき、すべての「継承可能な実装クラス」を介して具象実装クラスをテンプレート パラメータとしてインターフェイスに渡す代わりに、インターフェイスがテンプレート パラメータとして継承するリダイレクト クラスを渡します。
関数を「final」にしたい場合は、抽象的なリダイレクト クラスを継承する「リダイレクト オーバーライド クラス」を作成し、final にしたいリダイレクト関数をオーバーライドします。次に、継承可能なすべての実装クラスを通じて、この新しい「リダイレクト オーバーライド クラス」をパラメーターとして渡します。
このアプローチでは:
- 「最終」関数は、キャストを介してリダイレクトされるのではなく、直接呼び出されます (「最終」関数をリダイレクト オーバーライド クラスではなく、継承可能な実装クラスで実装する必要がある場合を除きます)。
- 「最終」関数は、将来のユーザー コードによってオーバーライドできません。
- 各「最終」関数は、継承のレベルごとに追加の ImplFinal クラスのみを必要とし、追加のボイラープレートは必要ありません。
これはすべて非常に複雑に聞こえるので、理解しやすくするために作成したフロー図を次に示します。
DImpl および EImpl には、DImpl または EImpl のいずれかが継承されている場合にオーバーライドできない最終関数があります。

コード例:
#include <iostream>
#include <type_traits>
template <class Top>
struct Redirect
{
protected:
// The "pure virtual functions"
inline void fooImpl()
{
top().fooImpl();
}
inline void fooImpl2()
{
top().fooImpl2();
}
inline void fooImpl3()
{
top().fooImpl3();
}
inline void fooImpl4()
{
top().fooImpl4();
}
inline Top& top()
{
// GCC doesn't allow static_cast<Top&>(*this)
// since Interface uses private inheritance
static_assert(std::is_base_of<Redirect, Top>::value, "Invalid Top class specified.");
return (Top&)(*this);
}
};
// Wraps R around the inner level of a template T, e.g:
// R := Redirect, T := X, then inject_type::type := Redirect<X>
// R := Redirect, T := A<B<C<X>>>, then inject_type::type := A<B<C<Redirect<X>>>>
template<template<class> class R, class T>
struct inject_type
{
using type = R<T>;
};
template<template<class> class R, class InnerFirst, class... InnerRest, template<class...> class Outer>
struct inject_type<R, Outer<InnerFirst, InnerRest...>>
{
using type = Outer<typename inject_type<R, InnerFirst>::type, InnerRest...>;
};
// We will be inheriting either Redirect<...> or something
// which derives from it (and overrides the functions).
// Use private inheritance, so that all polymorphic calls can
// only go through this class (which makes it impossible to
// subvert redirect overrides using future user code).
template <class V>
struct Interface : private inject_type<Redirect, V>::type
{
using impl = Interface;
void foo()
{
std::cout << "Calling Interface::foo()\n";
fooImpl();
}
void foo2()
{
std::cout << "Calling Interface::foo2()\n";
fooImpl2();
}
void foo3()
{
std::cout << "Calling Interface::foo3()\n";
fooImpl3();
}
void foo4()
{
std::cout << "Calling Interface::foo4()\n";
fooImpl4();
}
private:
using R = typename inject_type<::Redirect, V>::type;
protected:
using R::fooImpl;
using R::fooImpl2;
using R::fooImpl3;
using R::fooImpl4;
};
template<class V>
struct DefaultImpl : Interface<V>
{
template<class>
friend struct Redirect;
protected:
// Picking up typename impl from Interface, where all polymorphic calls must pass through
using impl = typename DefaultImpl::impl;
void fooImpl()
{
std::cout << "Default::fooImpl()\n";
}
void fooImpl2()
{
std::cout << "Default::fooImpl2()\n";
std::cout << "Calling foo() from interface\n";
impl::foo();
}
void fooImpl3()
{
std::cout << "Default::fooImpl3()\n";
std::cout << "Calling highest level fooImpl2() from interface\n";
impl::fooImpl2();
}
void fooImpl4()
{
std::cout << "Default::fooImpl4()\n";
std::cout << "Calling highest level fooImpl3() from interface\n";
impl::fooImpl3();
}
};
template<class V>
struct AImpl : public DefaultImpl<V>
{
template<class>
friend struct Redirect;
protected:
void fooImpl()
{
std::cout << "A::fooImpl()\n";
}
};
struct A : AImpl<A>
{
};
template<class V>
struct BImpl : public AImpl<V>
{
template<class>
friend struct Redirect;
protected:
BImpl()
: i{1}
{
}
private:
int i;
void fooImpl2()
{
std::cout << "B::fooImpl2(): " << i << "\n";
}
};
struct B : BImpl<B>
{
};
template<class V>
struct CImpl : public BImpl<V>
{
template<class>
friend struct Redirect;
protected:
CImpl(int x = 2)
: i{x}
{
}
private:
int i;
void fooImpl3()
{
std::cout << "C::fooImpl3(): " << i << "\n";
}
};
struct C : CImpl<C>
{
C(int i = 9)
: CImpl(i)
{
}
};
// Make D::fooImpl4 final
template<class V>
struct DImplFinal : public V
{
protected:
void fooImpl4()
{
std::cout << "DImplFinal::fooImpl4()\n";
}
};
// Wrapping V with DImplFinal overrides the redirecting functions
template<class V>
struct DImpl : CImpl<DImplFinal<V>>
{
};
struct D : DImpl<D>
{
};
template<class V>
struct EImpl : DImpl<V>
{
template<class>
friend struct Redirect;
protected:
void fooImpl()
{
std::cout << "E::fooImpl()\n";
}
void fooImpl3()
{
std::cout << "E::fooImpl3()\n";
}
// This will never be called, because fooImpl4 is final in DImpl
void fooImpl4()
{
std::cout << "E::fooImpl4(): this should never be printed\n";
}
};
struct E : EImpl<E>
{
};
// Make F::fooImpl3 final
template<class V, class Top>
struct FImplFinal : public V
{
protected:
// This is implemented in FImpl, so redirect
void fooImpl3()
{
top().fooImpl3();
}
// This will never be called, because fooImpl4 is final in DImpl
void fooImpl4()
{
std::cout << "FImplFinal::fooImpl4() this should never be printed\n";
}
inline Top& top()
{
// GCC won't do a static_cast directly :(
static_assert(std::is_base_of<FImplFinal, Top>::value, "Invalid Top class specified");
return (Top&)(*this);
}
};
// Wrapping V with FImplFinal overrides the redirecting functions, but only if they haven't been overridden already
template<class V>
struct FImpl : EImpl<FImplFinal<V, FImpl<V>>>
{
template<class>
friend struct Redirect;
template<class, class>
friend struct FImplFinal;
protected:
FImpl()
: i{99}
{
}
// Picking up typename impl from DefaultImpl
using impl = typename FImpl::impl;
private:
int i;
void fooImpl2()
{
std::cout << "F::fooImpl2()\n";
// This will only call DFinal::fooImpl4();
std::cout << "Calling fooImpl4() polymorphically. (Should not print FImplFinal::fooImpl4() or EImpl::fooImpl4())\n";
impl::fooImpl4();
}
void fooImpl3()
{
std::cout << "FImpl::fooImpl3(), i = " << i << '\n';
}
};
struct F : FImpl<F>
{
};
int main()
{
std::cout << "### A ###\n";
A a;
a.foo();
a.foo2();
a.foo3();
a.foo4();
std::cout << "### B ###\n";
B b;
b.foo();
b.foo2();
b.foo3();
b.foo4();
std::cout << "### C ###\n";
C c;
c.foo();
c.foo2();
c.foo3();
c.foo4();
std::cout << "### D ###\n";
D d;
d.foo();
d.foo2();
d.foo3();
d.foo4();
std::cout << "### E ###\n";
E e;
e.foo();
e.foo2();
e.foo3();
e.foo4();
std::cout << "### F ###\n";
F f;
f.foo();
f.foo2();
f.foo3();
f.foo4();
}
コードは以下を出力します。
### A ###
Calling CrtpInterface::foo()
A::fooImpl()
Calling CrtpInterface::foo2()
Default::fooImpl2()
Calling foo() from interface
Calling CrtpInterface::foo()
A::fooImpl()
Calling CrtpInterface::foo3()
Default::fooImpl3()
Calling highest level fooImpl2() from interface
Default::fooImpl2()
Calling foo() from interface
Calling CrtpInterface::foo()
A::fooImpl()
Calling CrtpInterface::foo4()
Default::fooImpl4()
Calling highest level fooImpl3() from interface
Default::fooImpl3()
Calling highest level fooImpl2() from interface
Default::fooImpl2()
Calling foo() from interface
Calling CrtpInterface::foo()
A::fooImpl()
### B ###
Calling CrtpInterface::foo()
A::fooImpl()
Calling CrtpInterface::foo2()
B::fooImpl2(): 1
Calling CrtpInterface::foo3()
Default::fooImpl3()
Calling highest level fooImpl2() from interface
B::fooImpl2(): 1
Calling CrtpInterface::foo4()
Default::fooImpl4()
Calling highest level fooImpl3() from interface
Default::fooImpl3()
Calling highest level fooImpl2() from interface
B::fooImpl2(): 1
### C ###
Calling CrtpInterface::foo()
A::fooImpl()
Calling CrtpInterface::foo2()
B::fooImpl2(): 1
Calling CrtpInterface::foo3()
C::fooImpl3(): 9
Calling CrtpInterface::foo4()
Default::fooImpl4()
Calling highest level fooImpl3() from interface
C::fooImpl3(): 9
### D ###
Calling CrtpInterface::foo()
A::fooImpl()
Calling CrtpInterface::foo2()
B::fooImpl2(): 1
Calling CrtpInterface::foo3()
C::fooImpl3(): 2
Calling CrtpInterface::foo4()
DImplFinal::fooImpl4()
### E ###
Calling CrtpInterface::foo()
E::fooImpl()
Calling CrtpInterface::foo2()
B::fooImpl2(): 1
Calling CrtpInterface::foo3()
E::fooImpl3()
Calling CrtpInterface::foo4()
DImplFinal::fooImpl4()
### F ###
Calling CrtpInterface::foo()
E::fooImpl()
Calling CrtpInterface::foo2()
F::fooImpl2()
Attempting to call FFinal::fooImpl4() or E::fooImpl4()
DImplFinal::fooImpl4()
Calling CrtpInterface::foo3()
FImpl::fooImpl3(), i = 99
Calling CrtpInterface::foo4()
DImplFinal::fooImpl4()