可以自动从模板类型中获取基类的类型吗?

时间:2012-01-03 07:54:46

标签: c++ templates metaprogramming

我正在尝试使用模板元编程来确定基类。有没有办法自动获取基类而不明确专门化每个派生类?

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
}

目前任何自动方法都需要选择第一个声明的基数,并且对于私人基地会失败。

6 个答案:

答案 0 :(得分:17)

可以使用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(&not_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(&not_deducible2::f)) x; // error: 'f' is ambiguous
}

在没有编译器支持的情况下,这是最好的。

答案 1 :(得分:5)

我不知道任何基类选择模板,我不确定是否存在或者甚至是个好主意。有许多方法可以破坏可扩展性并违背继承精神。当bar公开继承foo时,bar foo用于所有实际目的,客户端代码不需要区分基类和衍生类。

基类中的公共typedef经常会划伤您可能需要划伤并且更清晰的痒:

class foo { public: typedef foo name_making_type; ... };

int main() {
    Foo::name_making_type a;
    Bar::name_making_type b;
}

答案 2 :(得分:5)

我的解决方案不是真的自动,但我能想到的最好。

Intrusive 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;
};

答案 3 :(得分:2)

基类有什么用?你是.NET或Java程序员吗?

C ++支持多重继承,也没有全局公共基类。因此,C ++类型可能包含零个,一个或多个基类。因此禁止使用定冠词。

由于 基类没有意义,所以无法找到它。

答案 4 :(得分:1)

使用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作为protectedprivate继承,无需更改labeled_base类定义

该基础应该具有0运行时或空间开销,因为它的方法是内联的,什么都不做,并且没有自己的属性。

答案 5 :(得分:0)

我正在为类似问题寻找便携式解决方案数月。但我还没有找到它。

G ++有__bases__direct_bases。您可以将它们包装在类型列表中,然后访问其中的任何一个元素,例如带有std::tuple的{​​{1}}。有关用法,请参阅libstdc++'s <tr2/type_traits>

但是,这不便携。 Clang++ currently has no such intrinsics.