如 Evg 的 crtp-pass-types-from-driven-class-to-base-class 中所述,编译器无法推导出类
Impl::TType
声明中的类型 Base
:
template <typename Impl>
struct Base
{
typename Impl::TType t;
};
struct MyImplementation : public Base<MyImplementation>
{
using TType = int;
TType t;
MyImplementation(TType n) : t(n) {};
};
int main()
{
MyImplementation mi(3);
return 0;
}
但是,我想要一个 CRTP 设计,其中用户编写一个派生自
Base
的实现,唯一的要求是实现类指定类型 TType
,以便基类可以实例化该类型的成员。换句话说,类 Base
期望实现定义类型 TType
。例如,它可能必须对整数执行操作,但各种实现可以使用不同的 int 类型(例如 int、unsigned int、long、short ...)。因此,TType
是特定于实现的,不应该涉及 Base 类。粗暴的方法是:
template <typename Impl, typename T>
struct Base
{
T t;
};
struct MyImplementation : public Base<MyImplementation, T>
{
using TType = T; //uneccessary but left for consistency
TType t;
MyImplementation(TType n) : t(n) {};
};
但是这里类型
TType
确实 涉及 Base
并且留下了更多的错误空间。例如,如果用户想要模板 TType
,他/她需要编写 class MyImplementation : public Base< MyImplementation<T>, T >
,这是一个糟糕的 API(特别是如果 Base
需要定义多个类型;例如 public Base< MyImplementation<T, V, Q>, T, V, Q >
),使其成为容易犯错误,例如:
template <typename T, typename Q>
struct MyImplementation : public Base<MyImplementation<T, Q>, Q>
{
using TType = T;
};
有人知道现代有效的方法吗?
你可以写一个特征来从一些实例化
T
中推断出MyImplementation<T>
。
template <typename Impl> struct the_type;
template <typename T, template<typename> typename Impl>
struct the_type<Impl<T>> { using type = T; };
template <typename T> struct X {};
template <typename Impl>
struct Base {
using T = typename the_type<Impl>::type;
};
但是,这仅适用于具有单个类型参数的类模板。它可以变得更通用以允许任何数量,但是一旦用户模板混合类型和非类型模板参数,你就不走运了。此外,对于您的
Base
来说,Impl
和 T
之间的关系实际上并不重要。 Impl
可以是不是模板实例化的类型。 Impl
是否是SomeTemplate<T>
是一个实现细节。因此,我更喜欢你分别通过 Impl
和 T
的方法。