基类的类型能否从模板类型中自动获得?
Can the type of a base class be obtained from a template type automatically?
我试图使用模板元编程来确定基类。是否有一种方法可以自动获得基类,而无需显式特化每个派生类?
class foo { public: char * Name() { return "foo"; }; };
class bar : public foo { public: char * Name() { return "bar"; }; };
template< typename T > struct ClassInfo { typedef T Base; };
template<> struct ClassInfo<bar> { typedef foo Base; };
int main()
{
ClassInfo<foo>::Base A;
ClassInfo<bar>::Base B;
std::cout << A.Name(); //foo
std::cout << B.Name(); //foo
}
目前,任何自动方法都需要选择第一个声明的基,并且对于私有基会失败。
这在c++ 11和decltype
中是可能的。为此,我们将利用当成员从基类继承时,指向成员的指针不是指向派生类的指针。
struct base{
void f(){}
};
struct derived : base{};
&derived::f
的类型将是void (base::*)()
,而不是void (derived::*)()
。这在c++ 03中已经实现了,但是如果不实际指定基类类型,就不可能获得它。使用decltype
,这很容易,只需要这个小函数:
// unimplemented to make sure it's only used
// in unevaluated contexts (sizeof, decltype, alignof)
template<class T, class U>
T base_of(U T::*);
用法:
#include <iostream>
// unimplemented to make sure it's only used
// in unevaluated contexts (sizeof, decltype, alignof)
template<class T, class R>
T base_of(R T::*);
struct base{
void f(){}
void name(){ std::cout << "base::name()n"; }
};
struct derived : base{
void name(){ std::cout << "derived::name()n"; }
};
struct not_deducible : base{
void f(){}
void name(){ std::cout << "not_deducible::name()n"; }
};
int main(){
decltype(base_of(&derived::f)) a;
decltype(base_of(&base::f)) b;
decltype(base_of(¬_deducible::f)) c;
a.name();
b.name();
c.name();
}
输出:base::name()
base::name()
not_deducible::name()
正如最后一个例子所示,您需要使用的成员实际上是您感兴趣的基类的继承成员。
然而,有更多的缺陷:成员还必须明确地标识基类成员:
struct base2{ void f(){} };
struct not_deducible2 : base, base2{};
int main(){
decltype(base_of(¬_deducible2::f)) x; // error: 'f' is ambiguous
}
在没有编译器支持的情况下,这是你能得到的最好结果。
我的解决方案不是自动的,而是我能想到的最好的。
侵入式c++ 03解决方案:
class B {};
class A : public B
{
public:
typedef B Base;
};
非侵入式c++ 03解决方案:
class B {};
class A : public B {};
template<class T>
struct TypeInfo;
template<>
struct TypeInfo<A>
{
typedef B Base;
};
我不知道有任何基类选择模板,我也不确定是否存在这样的模板,甚至不确定这是不是一个好主意。这在很多方面破坏了可扩展性,违背了继承的精神。当bar
公开继承foo
时,bar
实际上是一个 foo
,客户端代码不需要区分基类和派生类。
基类中的公共类型定义通常可以解决您可能需要解决的问题,并且更清晰:
class foo { public: typedef foo name_making_type; ... };
int main() {
Foo::name_making_type a;
Bar::name_making_type b;
}
基类是怎么回事?你是。net或Java程序员吗?
c++支持多重继承,也没有全局通用基类。因此,c++类型可以有零个、一个或多个基类。因此,禁止使用定冠词。
由于基类没有意义,所以没有办法找到它
几个月来我一直在寻找类似问题的便携式解决方案。但是我还没有找到。
g++有__bases
和__direct_bases
。你可以将它们包装在一个类型列表中,然后访问其中的任何一个元素,例如std::tuple
和std::tuple_element
。参考libstdc++的<tr2/type_traits>
。
然而,这是不可移植的。clang++目前还没有这样的特性
在c++ 11中,当你的类只继承一个父类时,你可以创建一个具有base_t
成员的侵入式方法:
template<class base_type>
struct labeled_base : public base_type
{
using base_t = base_type; // The original parent type
using base::base; // Inherit constructors
protected:
using base = labeled_base; // The real parent type
};
struct A { virtual void f() {} };
struct my_class : labeled_base<A>
{
my_class() : parent_t(required_params) {}
void f() override
{
// do_something_prefix();
base_t::f();
// do_something_postfix();
}
};
对于这个类,您将始终拥有一个parent_t
别名,以调用父类构造函数,就好像它是base
构造函数一样,具有一个(可能)更短的名称,以及一个base_t
别名,以使您的类不知道基类类型名称,如果它很长或大量模板化。
parent_t
别名是受保护的,不会暴露给公众。如果您不希望base_t
的别名是公共的,您可以将labeled_base
继承为protected
或private
,而无需更改labeled_base
的类定义。
该基类应该没有运行时或空间开销,因为它的方法是内联的,不做任何事情,也没有自己的属性。
最近当我阅读虚幻引擎源代码时,我发现一段代码符合您的要求。简化代码如下:
#include <iostream>
#include <type_traits>
template<typename T>
struct TGetBaseTypeHelper
{
template<typename InternalType> static typename InternalType::DerivedType Test(const typename InternalType::DerivedType*);
template<typename InternalType> static void Test(...);
using Type = decltype(Test<T>(nullptr));
};
struct Base
{
using DerivedType = Base;
static void Print()
{
std::cout << "Base Logger" << std::endl;
}
};
struct Derived1 : Base
{
using BaseType = typename TGetBaseTypeHelper<Derived1>::Type;
using DerivedType = Derived1;
static void Print()
{
std::cout << "Derived1 Logger" << std::endl;
}
};
struct Derived2 : Derived1
{
using BaseType = typename TGetBaseTypeHelper<Derived2>::Type;
using DerivedType = Derived2;
static void Print()
{
std::cout << "Derived2 Logger" << std::endl;
}
};
int main()
{
Derived1::BaseType::Print();
Derived2::BaseType::Print();
}
使用下面的宏来包装这些代码使其变得简单:
#define DECLARE_BASE(T)
public:
using BaseType = typename TGetBaseTypeHelper<T>::Type;
using DerivedType = T;
当我第一次看到这些代码时,我感到困惑。看了@Xeo和@juanchopanza的回答后,我明白了。下面是它工作的关键:
- decltype表达式是成员声明的一部分不能访问之后声明的数据成员或成员函数它。
:在类Derived1的声明中,当声明Derived1::BaseType时,Derived1::BaseType不知道Derived1::DerivedType的存在。因为在Derived1::DerivedType之前声明了Derived1::BaseType。所以Derived1::BaseType的值是Base而不是Derived1
- 是否可以初始化不可复制类型的成员变量(或基类)
- 当基类是依赖类型时,这是一个缺陷吗
- 如何允许模板参数中的类类型,仅当它有两个基类时
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 当目标指针不是基类的类型时,为什么允许dynamic_cast为多态类生成 null 指针?
- 为什么允许dynamic_cast到非唯一的基类类型?
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 我们可以在不知道其真实类型的情况下将基类指针转换为派生类指针吗?
- 从基类指针派生派生类的模板类型
- 基类类型向量中的派生结构
- 从基类的唯一指针中声明派生类的类型
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 如何隐藏模板基类的参数类型
- 即使基类和派生类只使用基元数据类型,我是否需要定义虚拟析构函数
- 添加字符串类型的类成员会导致调用基类函数而不是子函数
- C++ 模板:重载时找不到基类类型参数方法
- 如何从包含基类指针的容器中调用派生类函数(基于其类型)?
- 为什么嵌套类型的基类不需要"typename"?
- 将空基类优化对象强制转换为另一种类型是否会破坏严格的别名?
- 从具有泛型返回类型的 crtp 基类调用派生类中的函数