Can the type of a base class be obtained from a template type automatically?

左心房为你撑大大i 提交于 2019-12-18 04:46:22

问题


I am trying to use template meta-programming to determine the base class. Is there a way to get the base class automatically without explicitly specializing for each derived class?

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
}

for right now any automatic method would need to select the first declared base and would fail for private bases.


回答1:


My solutions are not really automatic, but the best I can think of.

Intrusive C++03 solution:

class B {};

class A : public B
{
public:
    typedef B Base;
};

Non-intrusive C++03 solution:

class B {};

class A : public B {};

template<class T>
struct TypeInfo;

template<>
struct TypeInfo<A>
{
    typedef B Base;
};



回答2:


It's possible with C++11 and decltype. For that, we'll exploit that a pointer-to-member is not a pointer into the derived class when the member is inherited from a base class.

For example:

struct base{
    void f(){}
};
struct derived : base{};

The type of &derived::f will be void (base::*)(), not void (derived::*)(). This was already true in C++03, but it was impossible to get the base class type without actually specifying it. With decltype, it's easy and only needs this little function:

// unimplemented to make sure it's only used
// in unevaluated contexts (sizeof, decltype, alignof)
template<class T, class U>
T base_of(U T::*);

Usage:

#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();
}

Output:

base::name()
base::name()
not_deducible::name()

As the last example shows, you need to use a member that is actually an inherited member of the base class you're interested in.

There are more flaws, however: The member must also be unambiguously identify a base class member:

struct base2{ void f(){} };

struct not_deducible2 : base, base2{};

int main(){
  decltype(base_of(&not_deducible2::f)) x; // error: 'f' is ambiguous
}

That's the best you can get though, without compiler support.




回答3:


I am not aware of any base-class-selecting template, and I'm not sure one exists or is even a good idea. There are many ways in which this breaks extensibility and goes against the spirit of inheritance. When bar publicly inherits foo, bar is a foo for all practical purposes, and client code shouldn't need to distinguish base class and derived class.

A public typedef in the base class often scratches the itches you might need to have scratched and is clearer:

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

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



回答4:


What's with the base class? Are you a .NET or Java programmer?

C++ supports multiple inheritance, and also does not have a global common base class. So a C++ type may have zero, one, or many base classes. Use of the definite article is therefore contraindicated.

Since the base class makes no sense, there's no way to find it.




回答5:


With C++11, you can create a intrusive method to always have a base_t member, when your class only inherits from one parent:

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();
    }
};

With that class, you will always have a parent_t alias, to call the parent constructors as if it were the base constructors with a (probably) shorter name, and a base_t alias, to make your class non-aware of the base class type name if it's long or heavily templated.

The parent_t alias is protected to don't expose it to the public. If you don't want the base_t alias is public, you can always inherit labeled_base as protected or private, no need of changing the labeled_base class definition.

That base should have 0 runtime or space overhead since its methods are inline, do nothing, and has no own attributes.




回答6:


I am looking for a portable resolution for similar problems for months. But I don't find it yet.

G++ has __bases and __direct_bases. You can wrap them in a type list and then access any one of its elements, e.g. a std::tuple with std::tuple_element. See libstdc++'s <tr2/type_traits> for usage.

However, this is not portable. Clang++ currently has no such intrinsics.



来源:https://stackoverflow.com/questions/8709340/can-the-type-of-a-base-class-be-obtained-from-a-template-type-automatically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!