I have a template and many derived classes. In all of these classes I want to use the template type and some dependent types of it (actually I have many types and many dependent types).
The following approach is quite bad, since I need to manually inherit (with using keyword) all the types from the parent class (see code example):
EDIT: I didn't know that it is important that the class where I want to use the types is also a template. I updated the code example accordingly.
template<class T>
class A {
using TBase = T;
using TDerived = T[10];
}
template<class T>
class B : A<T> {
// NOTE i want the types TBase and TDerived here and in further children
using TBase = A<T>::TBase;
using TDerived = A<T>::TDerived
}
class C : B<int> {
// NOTE i want the types TBase and TDerived here and in further children
using TBase = B<int>::TBase;
using TDerived = B<int>::TDerived
}
I also thought about templated namespaces so that I could define the types outside of the class - but they don't exist.
Does anyone have a better idea?
SOLUTION1: fix current approach
(based on the comments from Jan Hudec and the answer from Oguk)
public inheritance
I forget the keyword public: for classes (struct is public by default).
missing typename
The compiler warning for my (wrong) types where used in the class where misleading (although the ones for the using keyword directly hint to the missing typename keyword).
Further notes about when to use typename can be found here: https://stackoverflow.com/a/7923419/3779655
type interitance
When I correctly specify the type (e.g. see 2. missing typename) then I can also use the current class name.
Resulting code:
template<class T>
class A {
public:
using TBase = T;
using TDerived = T[10];
};
template<class T>
class B : public A<T> {
public:
// possiblity 1:
using TBase = typename B::TBase;
TBase memberB1;
// possibility 2
typename B::TBase memberB2;
};
class C : public B<int> {
public:
// NOTE C is no template -> we don't have to do anything
TBase memberC;
};
SOLUTION2: trait classes
(based on the answer from Oguk)
use templated trait class instead of namespace
This gives pretty much the same functionality.
different availability of type information
One thing I didn't think of before is to check which type information is available where and if further characters are needed to retrieve the needed type information from a parent class (see NOTE comments).
See code below:
template<class T>
struct TypesContainer {
using TBase = T;
using TDerived = T[10];
};
template<class T>
class A {
public:
// TODO possible to directly inherit all types?
//using TypesContainer<T>;
// NOTE this is only possible for methods, not types
using Types = TypesContainer<T>;
typename Types::TBase memberA;
};
class B : public A<int> {
public:
// NOTE here I have all template information via definition (by hand)
using Types = TypesContainer<int>;
typename Types::TBase memberB;
};
class C : public B {
public:
// NOTE here I don't have the type information any more
using Types = TypesContainer<A::Types::TBase>;
typename Types::TBase memberC;
};
PROBLEM:
Is there a way to directly inherit all members of a namespace/class within the class scope (see TODO comment). Using the using keyword without assignement operator = does only work for methods not namespaces, classes and types.
First, if you are otherwise fine with your design, it should work if you inherit the base class publicly. This would work in the code you posted. However, if your example is very simplified, there might be another reason you cannot access the typedefs in your real code: The thing which could cause problems for you is if your derived class is actually also a templated class with the type parameter T, e.g. B<T>. This would make the base class A<T> dependent and its members would not considered during lookup if you use a non-dependent name (i.e. only TBase) to refer to them inside B<T>, so they would not be found. Referring to them as typename A<T>::TBase would solve that problem by making the name dependent names.
If you want to follow your other idea and have that type information available outside of the class (because it is conceptually more connected to T and not so much to the class A), instead of the "templated namespaces" you were thinking of, usually, trait classes are used for this purpose:
template <class T>
struct MyTraits {
using TBase = T;
using TDerived = T[10];
}
Then you can access those using declarations (or, alternatively, typedefs) from anywhere by using MyTraits<T>::TBase. This is effectively what you would achieve with a templated namespace. Don't forget to add the typename keyword if you use the using/typedef members of MyTraits in a dependent context, i.e. typename MyTraits<T>::TBase.
Related
I have some code like this
class A : public b<T>
{
public:
typedef b<T> _baseclass; // why we need this declaration
};
What is the use of typedef inside the class?
Is the definition limited to this class only?
Shall we create this as static and use without crating an object of the class?
This member type will be available outside of the class definition too, which is convenient in template code. If you passed an A into a function template, or maybe some other classes that also have _baseclass member types, then you can use _baseclass to find out what the base is without needing to know exactly what the top-level type is.
Standard templates like std::vector and std::map have member types like value_type — these do not signify a base class but have a similar purpose, in that you can use value_type anywhere a container is used, no matter which container is used.
Swapping typedef to using (because I want to), here's an example:
// The class templates
template <typename T>
struct Base {};
struct A : Base<int>
{
using base_class = Base<int>;
};
struct B : Base<char>
{
using base_class = Base<char>;
};
struct C : Base<bool>
{
using base_class = Base<bool>;
};
// The example
template <typename T>
void foo()
{
// typename needed because base_class is a "dependent name"
// (just go with it)
typename T::base_class the_base;
// This line is to suppress "unused variable" warnings
(void)the_base;
}
int main()
{
foo<A>();
foo<B>();
foo<C>();
}
Though this particular program doesn't actually "do anything", it shows a function template foo that can "know" what the base class was in each case, without any further information about exactly what T is. And it'll work for any class to which you've added a base_class member type!
Is it possible to check if a member variable, a member function, or a type definition is declared in a given class?
Various questions on StackOverflow talk about checking if a given class merely contains a member, essentially using std::is_detected. But all these solutions detect the member also in derived classes, which may not declare the member themselves.
For example, the following doesn't compile.
#include <experimental/type_traits>
struct base
{
using type = std::true_type;
};
struct derived : public base { };
template<typename T>
using has_type_t = typename T::type;
template<typename T>
constexpr inline bool has_type_v =
std::experimental::is_detected<has_type_t, T>::value;
int main ()
{
static_assert (has_type_v<base>);
static_assert (!has_type_v<derived>);
}
Can any changes be made so that the two assertions hold? Or is reflection needed for that?
I don't see a way for type or static member, but for regular member, you can have distinguish base::m from derived::m:
template<typename T>
using has_m_t = decltype(&T::m);
template<typename T>
constexpr inline bool has_m_v =
std::experimental::is_detected_exact<int (T::*), has_m_t, T>::value;
And the test:
struct base { int m; };
struct derived : public base {};
struct without {};
static_assert( has_m_v<base>);
static_assert(!has_m_v<derived>);
static_assert(!has_m_v<without>);
Demo
I'm inclined to say no. Proving that is of course hard, but I can explain why I think so.
C++ is a compiled language. The compiler has an internal representation of types, which you can't access directly. The only way to access this internal representation is through the facilities of the language. Actual implementations can vary in the way they represent types internally, and often do have additional information to produce better error messages. But this is not exposed.
So yes, most compilers can enumerate base types, and know exactly where each member of a class came from. That's essential for good error messages. But you can't enumerate base classes at compile time, using only C++ templates.
You might think that for data members you could try tricks with addresses and offsets. That won't work, again because you need to know the size of all base classes - and you can't enumerate those.
You might also consider tricks in which you create further-derived helper classes. That's possible, but they suffer from the same problem. You can only derive from the given type, not from its parent(s). It's thus possible to create a child class, but not a sibling. And that child inherits from parent and grandparent alike. Even if you wrote using derived::type in the helper class, that would find base::type.
You can but with a limitation, your compiler must have an intrinsic that provides the list of base classes (GCC provides it).
If you can have access to such en intrinsic, the only difficulty is to check that member access through the derived is not actualy an access to a member of the base.
To check this, the idea is to use the 'ambiguous' access that happens when accessing a member declared in multiple bases of a derived class:
struct base
{
using type = std::true_type;
};
struct Derived1 : base{
};
struct Derived2 : base{
using type = std::true_type;
};
struct Test1: Derived1,base{};
struct Test2: Derived2,base{};
void g(){
Test1::type a;
Test2::type b;//Do not compile: 'type' is ambiguous
}
So you can generalize this trick this way:
template<class T,class Base>
struct MultiDer: T,Base{};
template<class T,class Base,class=void>
struct has_ambiguous_type
:std::true_type{};
template<class T,class Base>
struct has_ambiguous_type
<T,Base,std::void_t<typename MultiDer<T,Base>::type>>
:std::false_type{};
template<class T,class=void>
struct has_type
:std::false_type{};
template<class T>
struct has_type
<T,std::void_t<typename T::type>>
:std::true_type{};
template<class T,class...Bases>
constexpr inline auto has_type_declared_imp =
has_type<T>::value
//if any ambiguous access happens then T has 'type'
&& ( (has_ambiguous_type<T,Bases>::value || ...)
//or no ambiguity happened because none of the base has 'type'
|| (!has_type<Bases>::value && ...));
template<class T>
constexpr inline auto has_type_declared =
has_type_declared_imp<T,__direct_bases(T)...>;//GCC instrinsic
static_assert(has_type_declared<Derived2>);
static_assert(!has_type_declared<Derived1>);
The only problem is portability: your compiler must provides a mechanism to get access to the list of direct bases of a type. Here I have used the GCC's intrinsic __direct_bases.
I caught myself "inventing" this simple construct lately when working with many templated classes and deriving from them. I am not sure if it is common practice, or am I tying a rope around my neck.
template <typename T> class Base {};
template <typename T> class Derived : public Base<T>{
typedef Base<T> Base;
};
I found it especially useful if the Base class has its own typedefs for some types. E.g:
template <typename T> class Base {
typedef T Scalar;
typedef Matrix<Scalar> Matrix;
};
Then it's easy to "import" types into the Derived. It saves re-typing the template signature. E.g:
template <typename T> class Derived : public Base<T>{
typename Base<T>::Matrix yuck_yuck(); //that's what I am trying to simplify
typedef typename Base<T> Base;
typedef typename Base::Matrix Matrix;
Matrix much_fun(); //looks way better
};
Also on of the big advantages is that, when you want to add another template parameter to the Base class. You don't have to go over a bunch of functions to change, just update the typedef. much_fun will have no problem if Base will be changed to Base<T,U> while yuck_yuck will need to have updated signatures (not sure if template parameter is formally included with the signature, so pardon me if I am making a formal error here, but I think it is).
Is this a good practice or am I playing with a gun next to my vital parts? It looks like it makes code more readable, and simplifies it, but maybe I am missing something that can backfire.
EDIT2: I got the working example. The Base class must be within its namespace or there will be conflicts with the same names within a scope, as the commenters pointed out. Below is the minimal example that embodies my real question.
namespace Fun {
template <typename T> class Base {
public:
typedef T Scalar;
};
}
template <typename T>
class Derived : public Fun::Base<T>{
public:
typedef typename Fun::Base<T> Base;
typedef typename Base::Scalar Scalar;
typename Fun::Base<T>::Scalar yuck_yuck();
Scalar much_fun();
};
#include <iostream>
using namespace std;
int main() {
Derived<double> d;
return 0;
}
With lots of stuff the code gets really bloated with typenames, and template parameters. But I already run into a trouble making up the example, by not placing Base in its own namespace. I wonder if there are any other caveats, that are actually killers to the idea.
I believe this is ill-formed, due to rule 2 of C++11 3.3.7/1
A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S.
meaning that you can't use the name Base to refer to both the template and the typedef within the class scope. Certainly, my compiler won't accept it:
error: declaration of ‘typedef struct Base<T> Derived<T>::Base’ [-fpermissive]
error: changes meaning of ‘Base’ from ‘struct Base<T>’ [-fpermissive]
(NOTE: this refers to the simplified example originally posted, and doesn't cover the updated question where the base class name is in a different scope.)
I actually consider it a helpful (and good) practice if the typedef is not exposed public or protected:
// No template, not Base, to avoid that discussion
class Derive : public SomeBaseClass
{
private:
typedef SomeBaseClass Base;
public:
typedef Base::T T;
T f();
};
class MoreDerived : public Derived
{
// Base is not accessible
};
In this piece I'm trying to declare in Class B a list that can hold objects of Class A of any type, such as A<int>, A<double>, A<float>. I intend to add A objects to the list during runtime:
#include <list>
template <class T> class A {};
class B {
template<class T> std::list<A<T>*> objects;
};
It seems like making a list like this should work but compiling it gives an error:
Line 6: error: data member 'objects' cannot be a member template
compilation terminated due to -Wfatal-errors.
Can somebody explain why this doesn't work and how I can fix it?
That's just not how C++ works. If you want to group different objects together, they need to have at least some relation. Being instantiations of the same class template doesn't imply that they are related, they're completely distinct types. If you want a list of A<T>*s, better make a list of base-class pointers and forward operations through virtual functions:
class A_base{
public:
virtual void foo() = 0;
virtual ~A_base() { }
};
template<class T>
class A : public A_base{
public:
void foo(){
// ...
}
};
class B{
std::list<A_base*> objects;
};
Member variables aren't allowed to be templates. Only member functions can be templates. You'll have to templatize the enclosing class B instead:
template <class T>
class B {
std::list<A<T>*> objects;
};
Unfortunately you cannot have template variables. Only option to declare a member data is to make the class template:
template<class T>
class B {
std::list<A<T>*> objects;
};
Depending on what you're doing, type erasure might be an option. On the Tension Between Object-Oriented and Generic Programming in C++ is my favorite write-up on the subject.
In a nutshell, you convert the static dispatch enabled by the templates into dynamic dispatch through a custom inheritance tree you setup on the fly. Instead of storing A<T>, you create a new type that has the common interface you desire, and using some template/inhertiance voodoo this new type stores an A<T> without actually exposing the T. So A<int> and A<double> and A<A<std::list<A<int> > > > and some_type_that_looks_like_A_but_really_isnt all reduce down to a single type.
But you have to have a common interface, independant of that parameter. If you can't, things get more difficult.
Boost.Any is a good example, as is std::shared_ptr [which uses type erasure to remember how to delete the pointer passed to it even in the face of non-polymorphic inheritance].
Make B class template just like you've made A a class template:
template<class T>
class B {
std::list<A<T>*> objects;
};
I have my templated container class that looks like this:
template<
class KeyType,
class ValueType,
class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>,
class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType>
>
class MyClass
{
[...]
}
Which means that when I instantiate an object of this class, I can do it several different ways:
MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;
Those are all good. The problem comes when I want to instantiate a MyClass that uses a non-default version of the ValueCompareFunctor argument, but I still want to use the default value of the KeyCompareFunctor argument. Then I have to write this:
MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;
It would be much more convenient if I could somehow omit the third argument and just write this:
MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;
Since the MyCustomValueCompareFunctor works only on objects of type MyValueType and not on objects of type MyKeyType, it seems like the compiler could at least theoretically work out what I meant here.
Is there a way to do this in C++?
In general, both in templates and functions or methods, C++ lets you use default for (and thereby omit) only trailing parameters -- no way out.
I recommend a template or macro to shorten AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> to Foo<MyKeyType> -- not perfect, but better than nothing.
No. The closest you can come is to allow users to specify some sentinel type - like void - meaning "use default value here", and use template metamagic inside your class to typedef the real default if void was given to you. But this probably isn't a good idea from readability point of view.
Boost parameters and Boost graph named parameters are efforts towards naming parameters for template functions/methods. They give the opportunity to provide arguments in whichever order you prefer. Some arguments may be optional, with default values.
The same approach may be applied to template arguments. Instead of having N template arguments + P optional ones, create your class with N+1 template arguments. The last one will hold "named" parameters which can be omitted.
This answer is not complete yet, but i hope it's a good start !
An alternative option is to use Traits classes:
template <class KeyType>
class KeyTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
};
template <class ValueType>
class ValueTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare;
};
template<class KeyType class ValueType>
class MyClass
{
typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
};
Then if you have a type which needs a different comparison function for Key's, then you'd explicitly specialize the KeyTraits type for that case. Here's an example where we change it for int:
template <>
class KeyTraits<int>
{
typedef SpecialCompareForInt Cmopare;
};
There is another option, which uses inheritance and which works like the following. For the last two arguments, it uses a class that inherits virtually from a class that has two member templates, that can be used to generate the needed types. Because the inheritance is virtual, the typedefs it declares are shared among the inheritance as seen below.
template<class KeyType,
class ValueType,
class Pol1 = DefaultArgument,
class Pol2 = DefaultArgument>
class MyClass {
typedef use_policies<Pol1, Pol2> policies;
typedef KeyType key_type;
typedef ValueType value_type;
typedef typename policies::
template apply_key_compare<KeyType>::type
key_compare;
typedef typename policies::
template apply_value_compare<ValueType>::type
value_compare;
};
Now, have a default argument that you use, which has typedefs for the default arguments you want provide. The member templates will be parameterized by the key and value types
struct VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType>
type;
};
template<typename ValueType>
struct apply_value_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>
type;
};
};
struct DefaultArgument : virtual VirtualRoot { };
template<typename T> struct KeyCompareIs : virtual VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef T type;
};
};
template<typename T> struct ValueCompareIs : virtual VirtualRoot {
template<typename ValueType>
struct apply_value_compare {
typedef T type;
};
};
Now, use_policies will derive from all the template arguments. Where a derived class of VirtualRoot hides a member from the base, that member of the derived class is dominant over the member of the base, and will be used, even though the base-class member can be reached by other path in the inheritance tree.
Note that you don't pay for the virtual inheritance, because you never create an object of type use_policies. You only use virtual inheritance to make use of the dominance rule.
template<typename B, int>
struct Inherit : B { };
template<class Pol1, class Pol2>
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
{ };
Because we potentially derive from the same class more than once, we use a class template Inherit: Inheriting the same class directly twice is forbidden. But inheriting it indirectly is allowed. You can now use this all like the following:
MyClass<int, float> m;
MyClass<float, double, ValueCompareIs< less<double> > > m;