The title almost says everything: Is there a way in C++ to get a class's base type(s) at compile time? I. e. is it possible to hand a class to a template, and let the template use other templates to which it hands the bases of the given class?
My question is not whether I can implement such a functionality myself, there is no question I can (using traits and the like). My question is whether there is some (obscure) builtin functionality that could be used to this end.
gcc supports this. See
Kerrek's answer
tr2/type_traits
Andy Prowl's code example
n2965
What is the status of N2965 - std::bases and std::direct_bases?
How to query for all base classes of a class at compile time?
n2965 provides an example.
This simple examples illustrates the results of these type traits. In
the Suppose we have the following class hierarchy:
class E {};
class D {};
class C : virtual public D, private E {};
class B : virtual public D, public E {};
class A : public B, public C {};
It follows that bases<A>::type is tuple<D, B, E, C, E>
Similarly, direct_bases<A>::type is tuple<B, C>
Andy Prowl's code example is as follows:
#include <tr2/type_traits>
#include <tuple>
template<typename T>
struct dbc_as_tuple { };
template<typename... Ts>
struct dbc_as_tuple<std::tr2::__reflection_typelist<Ts...>>
{
typedef std::tuple<Ts...> type;
};
struct A {};
struct B {};
struct C : A, B {};
int main()
{
using namespace std;
using direct_base_classes = dbc_as_tuple<tr2::direct_bases<C>::type>::type;
using first = tuple_element<0, direct_base_classes>::type;
using second = tuple_element<1, direct_base_classes>::type;
static_assert(is_same<first, A>::value, "Error!"); // Will not fire
static_assert(is_same<second, B>::value, "Error!"); // Will not fire
}
Related
Library code
My library has a CRTP class B<Derived>.
I created a Trait<T> class to enable user to change behavior of B.
The default setting is int. (#1)
#include <iostream>
#include <string>
//B and Trait (library class)
template<class Derived> class B;
template<class T>class Trait{
public: using type = int; //<-- default setting //#1
};
template<class Derived> class B{
public: using type = typename Trait<Derived>::type; //#2
public: type f(){return 1;}
};
User code ( full coliru demo )
Then, I create a new class C with a new setting std::string. (#3)
It works fine.
//C (user1's class)
template<class Derived> class C ;
template<class Derived>class Trait<C<Derived>>{
public: using type = std::string; //#3
};
template<class Derived> class C : public B<Derived>{};
Finally, I create a new class D.
I want D to derive C's setting i.e. std::string (not int).
However, it is not compilable at $.
//D (user2's class)
class D : public C<D>{ //#4
public: type f(){return "OK";} //#5
//$ invalid conversion from 'const char*' to 'B<D>::type {aka int}'
};
int main(){
D dt;
std::cout<< dt.f() <<std::endl;
}
My understanding
Roughly speaking, here is my understanding about the compile process :-
Just before class D (#4), it doesn't know about D.
At #4, to identity D::type, it looks up C<D>::type.
Finally, it finds that it is defined in B<D>::type at #2.
From #2, it travels to the definition at #1 and find type = int.
Thus D::type = int.
Note that #3 is ignored, because at this point (#4 and #5), D is still incomplete.
The compiler still doesn't fully recognize that D is derived from C<something> ... yet.
Question
How to let D automatically inherit Trait's setting from C without explicitly define another template specialization Trait<D>?
In other words, how to make #3 not ignored for D?
Trait is probably not a good design (?), but I prefer to let the type setting be in a separate trait class.
The instantiating goes like this:
D -> C<D> -> B<D> -> Traits<D>
Traits<D> does not match you partial specialization of Traits<C<Derived>>
If you change it to template<class Derived> class C : public B<C<Derived>>{}; that will in turn instantiate Traits<C<D>> and that will match your specialization and you get std::string as type.
To get the child from B you can use.
template <typename... T>
struct getChild;
template <template <typename... T> typename First, typename... Rest>
struct getChild<First<Rest...>> { using child = typename getChild<Rest...>::child; };
template <typename First>
struct getChild<First> { using child = First; };
and then add in
template<class Derived> class B{
public: using type = typename Trait<Derived>::type;
using child = typename getChild<Derived>::child;
public: type f(){return 1;}
};
I would like to achieve the following behavior
struct A {
};
template <bool arg>
struct B = A; // ERROR: THIS IS NOT A VALID LINE
template <>
struct B<false> {
// Specific implementation of B
};
In other words, if a template argument arg is true the struct B should match struct A exactly, otherwise I would like to provide my own implementation. Is there an elegant way to achieve that?
I can think of two possible approaches but neither attract me.
A workaround with help of using clause. I don't like this because I had to rename a class that contains a specific implementation of B. I don't want to introduce another struct C. It still should be called B not C.
struct A {
};
struct C {
// Specific implementation of B
};
template <bool arg>
using B = typename std::conditional<arg, A, C>::type;
Using inheritance. I don't like this approach because I want struct B to be an exact copy (alias) of struct A but not a derived class. Since constructors are not inherited I would probably have put extra lines to deal with that. All these extra lines do not bring any real functionality but just mimics the "assignment" operator that I need. Moreover, extra lines are source of possible overhead.
struct A {
};
template <bool arg>
struct B : public A
{
};
template <>
struct B<false> {
};
P.S. I am inclined to to use another namespace and using clause together
struct A {
};
namespace arg_false {
struct B {
// Specific implementation of B
};
}
template <bool arg>
using B = typename std::conditional<arg, A, arg_false::B>::type;
When using template like this:
class A {…}
class B : A {…}
class C : A {…}
template<typename T>
class D{…}
I need T can only be B or C. Which means T must be a derivation of A.
Is there any way to do this? Thanks!
Use std::is_base_of along with std::enable_if:
template<typename T, typename X = std::enable_if<std::is_base_of<A, T>::value>::type>
class D{...}
Note that it will accept any T as long as it derives from A. If you need T to be either B or C, then you need to modify it, and use std::is_same or/and std::conditional along with std::enable_if.
You could make it clean as:
template<typename T, typename Unused = extends<T,A>>
class D{...}
where extends is defined as:
template<typename D, typename B>
using extends = typename std::enable_if<std::is_base_of<B,D>::value>::type;
static_assert can also be used (like other answers have shown) if you want it to result in error and compilation failure. However if you need selection or deselection, say from many specializations, then use the above approach.
Hope that helps.
You can use static_assert in combination with std::is_base_of:
#include <type_traits>
class A {};
class B : A {};
class C : A {};
class X{};
template<typename T>
class D
{
static_assert(std::is_base_of<A,T>::value, "T must be derived from A");
};
int main()
{
D<C> d_valid;
D<X> d_fails; // compilation fails
return 0;
}
live on ideone
Yes, this should do it:
template<typename T>
class D {
static_assert(std::is_base_of<A,T>::value, "not derived from A");
// ...
};
Demo here.
But this is not the idea behind templates. If you write templated code, then it should be generic, I.e. work for all types that support the operations that you apply on them.
Assume the following class:
struct X : A, B, C{};
What problems might appear if I change it to the following?
struct indirect_C : C{};
struct indirect_BC : B, indirect_C{};
struct X : A, indirect_BC{};
The example may seem contrived, but it happens when you to inherit a variable number of bases through variadic templates, and also want functionality from those bases made available (or not) in the derived class.
template<class... List>
struct X : List...{
using List::something...; // ill-formed in C++11
};
As such, you need a work-around, which is inheriting "recursively" and bringing the functionality into scope at every recursive step:
template<class Head, class... Tail>
struct inherit_indirect
: Head
, inherit_indirect<Tail...>
{
using Head::something;
using inherit_indirect<Tail...>::something;
};
template<class T>
struct inherit_indirect<T>
: T
{
using T::something;
};
(See for example this answer of mine, where I used this technique.)
You cannot directly initialize class base objects B and C anymore in the constructor of X.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Is there a way to prevent a class from being derived from twice using a static assert and type trait?
What I'd like to prevent is more than one of the C based template from being derived in D (i.e. there should only ever be one instance of C derived from). Was hoping for maybe a static assert in C or B that may solve this.
// My Classes
template <class T>
class A {};
class B {};
template <class T, class S>
class C : public B, public virtual A<T> {};
// Someone elses code using my classes
class D : public C<Type1, Type2>, public C<Type3, Type4>
{
};
As it stands, it's impossible for B or C to detect what else a more derived class inherits from, so you can't add an assertion there. However, by adding a "curiously recursive" template parameter, you can tell C what the derived class is. Unfortunately, this does require the derived class to give the correct template argument, and there's no way to enforce that.
You can then determine whether the derived class inherits from B in more than one way; it is a base class, but you can't convert a derived class pointer to B* (since that conversion is ambiguous). Note that this doesn't necessarily indicate multiple inheritance; the test will also fail if there's non-public inheritance.
So the best solution I can think of is:
#include <type_traits>
template <class T> class A {};
class B {};
template <class T, class S, class D>
class C : public B, public virtual A<T> {
public:
C() {
static_assert(
std::is_base_of<C,D>::value && std::is_convertible<D*,B*>::value,
"Multiple inheritance of C");
}
};
struct Type1 {};
struct Type2 {};
struct Type3 {};
struct Type4 {};
class Good : public C<Type1, Type2, Good> {};
class Evil : public C<Type1, Type2, Evil>, public C<Type3, Type4, Evil> {};
int main()
{
Good good;
Evil evil; // Causes assertion failure
}
I had to put the assertion in the constructor rather than the class definition, since some of the types are incomplete when the class template is instantiated. Unfortunately, this means that the error will only be reported for classes that are actually instantiated.