给定一个使用CRTP的基类,我正在寻找在基本模板类中声明一个类型依赖于派生类的成员。
虽然以下工作符合预期:
template <class T> class BaseTraits;
template <class T> class Base {
using TypeId = typename BaseTraits<T>::TypeId;
TypeId id;
public:
Base() { id = 123; }
TypeId getId() { return id; }
};
class Derived;
template <> class BaseTraits<Derived> {
public:
using TypeId = int;
};
class Derived : public Base<Derived> {};
int main(int argc, char ** argv) {
Derived foo;
return foo.getId();
}
我想知道我是否可以简化实施。我可以在Base
模板中添加第二个模板参数,并使BaseTraits
更简单甚至摆脱它。但是,上面的代码段已经尝试删除第二个模板参数。我正在寻找不涉及Base
的第二个模板参数的解决方案。
我尝试了类似下面的内容,但它没有编译:
错误:无效使用不完整类型'类Derived'
template <class T> class Base {
using TypeId = typename T::TypeId;
TypeId id;
public:
Base() { id = 123; }
TypeId getId() { return id; }
};
class Derived : public Base<Derived> {
public:
using TypeId = int;
};
int main(int argc, char ** argv) {
Derived foo;
return foo.getId();
}
更新
Base
必须是模板。答案 0 :(得分:3)
是否可以使成员类型直接依赖于派生类?将appart的成员函数的结果类型设为auto
(推导出的返回类型),是不可能的。
因此,您的解决方案中使用 type-trait 是最佳且唯一的解决方案。
原因是在定义派生类时,基类必须是完整类型:编译器必须首先实例化并解析基类定义,然后才能解析派生类定义C++ standard N4140 [derived.class]/2(粗体是我的):
由base-type-specifier表示的类型应为不是未完全定义的类的类类型; [...]
答案 1 :(得分:2)
这样的事情:
<custom-element></custom-element>
<fake-element></fake-element>
<p></p>
<font-face></font-face>
答案 2 :(得分:1)
这种情况有点简化,但你付出了一些代价。
#include <any>
template <class T> class Base {
std::any id; // expensive, but cannot have T::TypeId here
public:
Base() : id(123) {}
auto getId() {
return std::any_cast<typename T::TypeId>(id);
} // T::TypeId is OK inside a member function
};
class Derived : public Base<Derived> {
public:
using TypeId = int;
};
答案 3 :(得分:1)
为什么不撤销类层次结构?
template <class T>
class Base : T {
using TypeId = typename T::TypeId;
TypeId id;
public:
Base() { id = 123; }
TypeId getId() { return id; }
};
struct BasicDerived {
using TypeId = int;
};
using Derived = Base<BasicDerived>;
答案 4 :(得分:0)
说实话,你已经遇到了硬循环依赖的问题。 任何出路都会有臭味 两个模板参数最终似乎是一个很小的代价。
你可以声明一个带有Derived和TypeID的虚拟模板类吗?不过,我认为它不会让你获益。
TypeID:派生1:1映射?将另一个帮助器模板的1:1映射过度表示为从TypeID派生的后备查找会感觉更好吗?请注意,需要在Derived类之外定义TypeID才能执行此操作 TypeID真的需要在类中定义吗?它可以从Base中取出传入的定义来支持内部typedef的现有用法吗?
你可以双重包括吗?拆分或编写您的derived定义,以便typeid在基类定义中,可以包含在模板之前?这个DerivedBase可以在命名空间中声明,并包含一个返回完整Derived类的typedef链接,因此Base可以找到它以供参考。
答案 5 :(得分:0)
实际上,我想了一些......这不太令人不愉快:
你可以有一个绑定结构,甚至可以写成宏,在真正的类之前声明
绑定结构定义枚举,并为实际类定义不完整的typedef
模板在所有这些之前定义,但使用typename来推迟其依赖关系,但它由真实类实例化,并且仅依赖于绑定结构
template <class ThatClassWrapper>
class MyBase
{
protected:
typedef typename ThatClassWrapper::TypeId TypeId;
typedef typename ThatClassWrapper::RealClass ThatClass;
TypeId typeIdValue;
TypeId GetTypeId() { return typeIdValue; }
std::vector<ThatClass*> storage;
};
class SomeClass;
namespace TypeIdBinding
{
struct SomeClass
{
enum TypeId
{
hello, world
};
typedef ::SomeClass RealClass;
};
}
class SomeClass: public MyBase<TypeIdBinding::SomeClass>
{
public:
bool CheckValue(TypeId id)
{ return id == typeIdValue; }
};
请注意,真实类使用模板库中定义的TypeId,并且命名成员不能直接显示。您可以通过使模板Base派生自绑定结构(确认它以这种方式编译)来解决这个问题。虽然我真的喜欢在c ++ 11中,你可以从另一个命名空间中导出或者只输入enum typename,并使用该类型名作为枚举成员的前缀,有助于避免名称污染。