This question is for C++03, not C++11.
I have a case where I am using CRTP with multiple inheritance, and I am curious to know if there is a way to remove the redundancy that is created when specifying the type of B
below.
#include "boost/typeof/typeof.hpp"
#include "boost/units/detail/utility.hpp"
#include <iostream>
#include <string>
struct One{};
struct Two{};
template<typename T>
struct Type
{
static std::string name(void)
{
return boost::units::detail::demangle(typeid(T).name());
}
};
template<typename T1,
typename T2>
struct A
{
typedef A<T1, T2> Self;
A()
{
std::cout << Type<Self>::name() << std::endl;
}
};
template<typename T1,
typename T2,
typename T3>
struct B : public A<One, B<T1, T2, T3> >, // The B<T1, T2, T3> here is redundant
public A<Two, B<T1, T2, T3> >
{
typedef B<T1, T2, T3> Self;
B()
{
std::cout << Type<Self>::name() << std::endl;
}
};
int main(int argc, char* argv[])
{
B<int, int, int> t;
return 0;
}
See this on Coliru
The problem worsens when the number of template parameters for B
increases, when the template arguments themselves are complex, and when B
inherits from A
more times. I'd like to minimize the repetition of B
's template parameters. Specifically, I am looking for a way to access the typedef B<T1, T2, T3> Self
up in the inheritance list for B
, or some equivalent compile-time version of this
.
I cannot:
- Make a typedef for
B
above B
using a forward declaration, because I don't have access to the template parameters
- Make a typedef inside of the inheritance definition because the syntax doesn't allow that
- Access the typedef from inside the class, because it doesn't exist yet
Something like the below (none of which are not valid code, but display the effect I am looking for):
template<typename T1,
typename T2,
typename T3>
struct B : public A<One, Self>, // Cannot access the typedef yet
public A<Two, Self>
{
typedef B<T1, T2, T3> Self;
};
template<typename T1,
typename T2,
typename T3>
struct B : typedef B<T1, T2, T3> Self, // Invalid syntax
public A<One, Self>,
public A<Two, Self>
{
};
template<typename T1,
typename T2,
typename T3>
struct B : public A<One, B>, // I wish this would work
public A<Two, B>
{
};
template<typename T1,
typename T2,
typename T3>
struct B : public A<One, BOOST_TYPEOF(*this)>, // lol
public A<Two, BOOST_TYPEOF(*this)>
{
};
Is there a way to access a compile-time version of this
?
The problem with:
template<typename T1,
typename T2,
typename T3>
struct B : public A<One, B>, // I wish this would work
public A<Two, B>
{
};
is that your template <typename T1, typename T2> struct A
asks to
be instantiated with T2
a type, whereas what you wish you could do
is instantiate it with T2
a template, namely
template<typename, typename,typename> struct B
.
If the definition of A
lies in your own control, then perhaps - though perhaps not - a solution
is to make the definition of A
consistent with your wish:
#include "boost/typeof/typeof.hpp"
#include "boost/units/detail/utility.hpp"
#include <iostream>
#include <string>
struct One{};
struct Two{};
template<typename T>
struct Type
{
static std::string name(void)
{
return boost::units::detail::demangle(typeid(T).name());
}
};
template<typename T1,
template<typename, typename, typename> class T2
>
struct A
{
A()
{
std::cout << Type<A>::name() << std::endl;
}
};
template<typename T1,
typename T2,
typename T3>
struct B : public A<One, B >,
public A<Two, B >
{
B()
{
std::cout << Type<B>::name() << std::endl;
}
};
int main(int argc, char* argv[])
{
B<int, int, int> t;
return 0;
}
This program prints:
A<One, B>
A<Two, B>
B<int, int, int>
The price of this solution is restricting the classes for which
A
can furnish a CRTP base to ones that instantiate a template, like
B
, of exactly three typename
parameters.
Perhaps you are lucky enough that this restriction does not thwart any other
wishes you have. But if you also need A
to furnish a CRTP base
for classes that instantiate some template that does not have exactly three
typename
parameters, then it bites.
Provided that all of the classes for which you need A
to furnish a CRTP
base are instantiations of templates that have only typename
parameters,
and have at most N
of them, then you can still have a C++03 solution in the
same spirit:
You define A
as per the schema:
template<typename T1,
template<typename /*1*/,.... typename /*N*/> class T2
>
struct A { ... };
And for each template Y
for which A
is to be a CRTP base, you provide
exactly N
parameters, employing "padding" parameters that default to
void
, as necessary. For example, if N
== 3:
#include "boost/typeof/typeof.hpp"
#include "boost/units/detail/utility.hpp"
#include <iostream>
#include <string>
struct One{};
struct Two{};
template<typename T>
struct Type
{
static std::string name(void)
{
return boost::units::detail::demangle(typeid(T).name());
}
};
template<typename T1,
template<typename, typename, typename> class T2
>
struct A
{
A()
{
std::cout << Type<A>::name() << std::endl;
}
};
template<typename T1, typename T2 = void, typename T3 = void>
struct B : public A<One, B >,
public A<Two, B >
{
B()
{
std::cout << Type<B>::name() << std::endl;
}
};
template<typename T1, typename T2, typename T3 = void>
struct C : public A<One, C >,
public A<Two, C >
{
C()
{
std::cout << Type<C>::name() << std::endl;
}
};
template<typename T1, typename T2, typename T3>
struct D : public A<One, D >,
public A<Two, D >
{
D()
{
std::cout << Type<D>::name() << std::endl;
}
};
int main(int argc, char* argv[])
{
B<int> b;
C<int,int> c;
D<int,int,int> d;
return 0;
}
This program prints:
A<One, B>
A<Two, B>
B<int, void, void>
A<One, C>
A<Two, C>
C<int, int, void>
A<One, D>
A<Two, D>
D<int, int, int>
True, the more general solution sticks you with a different kind of "redundancy", in
the form of those superfluous defaulted template parameters. But you
may find it a less irksome kind.
(gcc 5.1/clang 3.6, C++03)