Having a template refer to a member from a jointly-inherited class - c++

It's obvious that the following code won't compile, because it gives an "undeclared identifier" error at the line 'n = n_init'. Nevertheless, to a human reader the intent is probably clear enough: I want to declare a template for a class which will never be instantiated by itself, but only by multiple inheritance alongside another class which is guaranteed to contain at minimum a member 'n' of type int and a member 'p' of type T*, but which (being a C struct obtained from elsewhere) I'm not at liberty to derive from another template containing these fields:
// In a C header file from elsewhere:
// ----------------------------------
typedef struct {
float *p;
int n;
} float_array_C;
// In my C++ header file:
// ----------------------
template<typename T> class MyArray
{
public:
MyArray(int n_init)
{
n = n_init;
contents.resize(n);
p = contents.data();
}
virtual void mustExist() = 0;
private:
std::vector<T> contents;
};
class float_array : public float_array_C, public MyArray<float>
{
public:
float_array(int n) : float_array_C(), MyArray(n)
{}
virtual void mustExist() {}
};
...
float_array testArray(10);
I've also tried this approach, with equally little success:
typedef struct {
float *p;
int n;
} float_array_C;
template<typename T1, typename T2> class MyArray
{
public:
MyArray(int n_init)
{
&T2::n = n_init;
contents.resize(n);
&T2::p = contents.data();
}
private:
std::vector<T1> contents;
};
typedef MyArray<float, float_array_C> floatArray;
...
float_array testArray(10);
Can this, or anything remotely similar to it, in fact be done?

In order for this to work, the template class must derive from the type containing n and then you can access it as T::n where T is the template parameter.
(You can't access the inherited member using just n because that's not a dependent name and so the compiler will try to resolve it when the template itself is compiled, not later when it is instantiated, and no n exists within MyArray or at the global scope. Using T::n causes it to be a dependent name -- depending on T -- and so resolution of the name is deferred until the template is instantiated.)
typedef struct {
float *p;
int n;
} float_array_C;
template <typename T>
class MyArray : public T
{
public:
MyArray(int n_init) {
T::n = n_init;
}
};
Note that you will run into problems with code like this:
class Foo : public float_array_C, public MyArray<float_array_C> { /* ... */ };
In this case both Foo and MyArray<float_array_C> contain a separate instance of float_array_C. You can use virtual inheritance for float_array_C if this is a problem:
template <typename T>
class MyArray : virtual public T { /* ... */ };
class Foo :
virtual public float_array_C,
public MyArray<float_array_C>
{ /* ... */ };

Another approach which only needs one template argument:
typedef struct {
float *p;
int n;
} float_array_C;
template<typename T> class MyArray : public T
{
public:
MyArray(int n_init)
{
T::n = n_init;
contents.resize(T::n);
T::p = contents.data();
}
private:
std::vector<std::remove_pointer_t<decltype(T::p)>> contents;
};
typedef MyArray<float_array_C> floatArray;

Related

Use Forwardly Declared Template Class in Header C++

I have a sprite class, which has a templatised data member. It holds an object, which has a pointer to this specialised sprite template class.
That object requires a forward declaration of my sprite class, but since sprite is a template class, I need to include the full header. Therefore I get a cyclic dependancy which I am unable to figure out
Sprite.h
#include "myclass.h"
template<typename SpriteType, typename = typename std::enable_if_t<std::is_base_of_v<sf::Transformable, SpriteType> && std::is_base_of_v<sf::Drawable, SpriteType>>>
class Sprite {
public:
SpriteType s;
myclass<SpriteType>;
Sprite() {
}
auto foo() {
return s;
}
private:
};
myclass.h
#include "Sprite.h"
//a sprite of type T, is going to create a myclass<Sprite<T>>, a pointer of the Sprite<T> is held in myclass.
template<typename T>
class myclass
{
public:
std::shared_ptr<Sprite<T>> ptr;
myclass() {
}
private:
};
How could I solve this cyclic dependency?
So in summary:
-Sprite is a template class.
-Sprite holds an object to another class. This other class holds a pointer to my this templated sprite class.
-This gives me a cyclic dependency, since both classes are now templates, and need to have their implementations written in their header files.
Simplified decoupling, based on #Taekahns solution.
template<typename T>
class myclass
{
public:
std::shared_ptr<T> ptr;
myclass() {
}
private:
};
template<typename SpriteType, typename = typename std::enable_if_t<std::is_base_of_v<sf::Transformable, SpriteType> && std::is_base_of_v<sf::Drawable, SpriteType>>>
class Sprite {
public:
SpriteType s;
// DO NOT PASS SpriteType here, put the whole Sprite<SpriteType>
myclass<Sprite<SpriteType>> t;
Sprite() {
}
auto foo() {
return s;
}
private:
};
One of the great thing about templates is breaking type dependencies.
You could do something like this. Simplified for readability.
template<typename T>
class myclass
{
public:
std::shared_ptr<T> ptr;
myclass() {
}
private:
};
template<typename SpriteType, typename = std::enable_if_t<std::is_base_of_v<base_class, SpriteType>>>
class Sprite {
public:
SpriteType s;
myclass<Sprite<SpriteType>> t;
Sprite() {
}
auto foo() {
return s;
}
private:
};
That is one of many options.
Another option is to use an interface. i.e. a pure virtual base class that isn't a template.
Example:
I think something like this should do it. Starting to get a hard to follow though.
class base_sprite
{
public:
virtual ~base_sprite(){};
virtual int foo() = 0;
};
template<typename T>
class myclass
{
public:
std::shared_ptr<base_sprite> ptr;
myclass() : ptr(std::make_shared<T>())
{
};
};
template<typename SpriteType>
class Sprite : public base_sprite{
public:
myclass<Sprite<SpriteType>> l;
int foo() override {return 0;};
};

inheritance of private member variable from class template

I made a class template looks like below, as base class for other classes to inherit from, and it would work fine as expected.
But my question is, The code still compiles even if I change 'protected' of class 'Operation' to 'private', even Matmul(which inherits class 'Operation') is modifying vector called 'edgeIn' which is declared as 'private'.
I can't understand why something like this should be allowed...
Shouldn't compiler trigger error message on this? (derived class shouldn't modify private member of base class)
template<typename T>
class Operation{
private: //Would compile fine even if I change this to 'private!'
class edge{
public:
edge(Tensor<T> tensor, Operation<T> &from, Operation<T> &to) {
this->tensor = tensor;
this->from = from;
this->to = to;
}
Operation<T> from;
Operation<T> to;
Tensor<T> tensor;
};
std::vector<edge> edgeIn; //edges as inputs of this operation
std::vector<edge> edgeOut; //edges as outputs of this operation
private:
//disable copy constructor (NOT ALLOWED)
Operation(Operation<T>& rhs) = default;
//disable move operator (NOT ALLOWED)
Operation<T>& operator=(Operation<T> &rhs) = default;
int operationId;
};
template<typename T>
class Matmul: public Operation<T>{
public:
Matmul(std::initializer_list<std::pair<Tensor<T>, Operation<T>>> args);
};
template<typename T>
//from Operation<T>, to This operation
Matmul<T>::Matmul(std::initializer_list<std::pair<Tensor<T>, Operation<T>>> args){
for(auto elem: args){
typename Operation<T>::edge info{elem.first, elem.second, *this};
this->edgeIn.emplace_back(info); //modifying member of base class
}
}
In the code you've shown, it's allowed because it's not wrong. Here's a simpler example:
template <class Ty>
class base {
int i; // private
};
template <class Ty>
class derived : base {
void set(int ii) { i = ii; }
};
At this point, if you write
derived<int> di;
di.set(3); // illegal: i is not accessible
you'll get an access error, as you expect.
But the original template isn't wrong, because the code might do this:
template <>
class base<int> {
public:
int i;
};
Now you can write
derived<int> di;
di.set(3);
and it's okay, because i is public in base<int>. You still can't write
derived<double> dd;
dd.set(3); // illegal: i is not accessible

Can I declare a different typedef depending on class?

Is it possible to have a different type definition based on which derived class is instantiated?
Say I have a parent class with a virtual function func(), two int members and a vector of type myType, and two child classes, which share the same int members, and the vector, but their implementation of func() require myType to be slightly different.
For example:
class Parent {
protected:
int myMember;
int myOtherMember;
std::vector<myType> vec;
public:
Parent(variable);
virtual int func() = 0;
}
class Child1 : public Parent {
private:
typedef <some type definiton> myType;
public:
Child1(variable) : Parent(variable){};
int func() {return someFunc();};
}
class Child2 : public Parent {
private:
typedef <some other type definiton> myType;
public:
Child2(variable) : Parent(variable){};
int func() {return someOtherFunc();};
}
Can I do something like this? when I have tried it, it creates a circular dependency in the header files, because class Parent is required to be included first, but then it requires myType to be defined.
Is there a way of forward declaring myType depending on class? or do I just need to include a different vector of myType in each class like so:
class Parent {
protected:
int myMember;
int myOtherMember;
public:
Parent(variable);
virtual int func() = 0;
}
class Child1 : public Parent {
private:
typedef <some type definiton> myType;
std::vector<myType> vec;
public:
Child1(variable) : Parent(variable){};
int func() {return someFunc();};
}
class Child2 : public Parent {
private:
typedef <some other type definiton> myType;
std::vector<myType> vec;
public:
Child2(variable) : Parent(variable){};
int func() {return someOtherFunc();};
}
This is a job for templateman!
First we declare Parent
template <class TYPE>
class Parent {
protected:
int myMember;
int myOtherMember;
std::vector<TYPE> vec;
public:
Parent(TYPE variable);
virtual int func() = 0;
};
Unfortunately a custom type contained within Child1 is out with this approach because the type needs to be declared before we can specialize a template on it. I'm using int and double here for ease. Replace as needed.
using Child1Type = int;
// or typedef int Child1Type; pre-C++11
class Child1 : public Parent<Child1Type> {
public:
Child1(Child1Type variable) : Parent(variable){};
int func() {return someFunc();};
};
using Child2Type = double;
class Child2 : public Parent<Child2Type> {
public:
Child2(Child2Type variable) : Parent(variable){};
int func() {return someOtherFunc();};
};
EDIT
Kicking myself for not getting this earlier (and burning way too much time because I knew it had to be possible, but was thinking in the wrong direction)
Declare the type up in Parent. Set the access based on who need to be able to see the type. Here I made Type public to test it out in main. The Type is declared in Parent and is visible to (and through because it's public) the Children.
Also stripped out everything that wasn't immediately necessary and used fixed width integer types so I could easily demonstrate the differences.
I think this is exactly what Pixelchemist was alluding to.
template <class TYPE>
class Parent {
public:
using Type = TYPE; // declare Type here
protected:
int myMember;
int myOtherMember;
std::vector<Type> vec; // visible here
public:
Parent(Type variable); // here
virtual ~Parent(){}
virtual int func() = 0;
};
class Child1 : public Parent<uint16_t> { // complicated type needs to be reproduced
// only once, here in the specialization
public:
Child1(Type variable) : Parent(variable){};
};
class Child2 : public Parent<uint32_t> {
public:
Child2(Type variable) : Parent(variable){};
};
int main()
{
// and visible way down here in main through Child1 and Child2
std::cout << "Child 1: " << sizeof(Child1::Type) << std::endl;
std::cout << "Child 2: " << sizeof(Child2::Type) << std::endl;
}
output is
Child 1: 2
Child 2: 4
You could simply use templates:
template<class T>
struct Base
{
std::vector<T> v;
};
struct D1 : public Base<int>
{
// all stuff in here comes into play when deriving from Base is already done
// Thus, compiler cannot know any typedefs from here inside Base...
};
struct D2 : public Base<double>
{
};
where you cannot use a typedef from derived in base class. See CRTP refer to typedef in derived class from base class.
This is what templates are for:
<template typename T>
class Child : public Parent {
private:
std::vector<T> vec;
public:
Child(variable) : Parent(variable) {};
int func() { return someOtherFunc(); };
}
Then Child is declared as something like:
Child<int> myChildInstance(10);

Can't use struct in derived templated class?

Ok, maybe not the best title, but here's the deal:
I have a templated interface:
template<typename T>
class MyInterface
{
public:
struct MyStruct
{
T value;
};
virtual void doThis(MyStruct* aPtr) = 0;
};
and an implementation:
template <typename T>
class MyImpl : public MyInterface<T>
{
public:
void doThis(MyStruct* aPtr)
{
} // doThis
};
However, the compiler complains:
In file included from MyTest.cpp:3:0:
MyImpl.h:7:17: error: ‘MyStruct’ has not been declared
void doThis(MyStruct* aPtr)
Why is that?
The following compiled for me:
template<typename T>
class MyInterface
{
public:
struct MyStruct
{
T value;
};
virtual void doThis(MyStruct* aPtr) = 0;
};
template <typename T>
class MyImpl : public MyInterface<T>
{
public:
void doThis(typename MyInterface<T>::MyStruct* aPtr)
{
}
};
int main() {
MyImpl<int> t;
}
The main change is that you need to qualify that the MyStruct was defined within MyInterface<T>.
Since the compiler cannot determine what kind of identifier is the templated subtype, you must help it using the typename keyword. (See When is the "typename" keyword necessary? for more details on typename)

C++ interface style programming. Need a way out

template <typename T>
class BaseQueue
{
public :
virtual void push_back(T value) = 0;
//other virtual methods
};
template <typename T>
class BaseDeque: public virtual BaseQueue<T>
{
public:
virtual void push_front(T value) = 0;
//other virtual methods
};
//Realisation
template <typename T>
class VectorQueue: public BaseQueue<T>
{
typedef typename std::vector<T> array;
private: array adata;
public:
VectorQueue()
{
adata = array();
}
void push_back(T value)
{
adata.push_back(value);
}
};
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>//,
{
void push_front(T value)
{
VectorQueue::adata.push_front(value);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
VectorDeque<int> vd = VectorDeque<int>();//here is a error
int i;
std::cin >> i;
return 0;
}
I have such error: "C2259: 'VectorDeque' : cannot instantiate abstract class ...". How can I fix it? Class VectorQueue has realize every virtual method of BaseQueue class already. But the compiler doesn't know it. The only way I see is to write something like this:
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>//,
{
void push_front(T value)
{
VectorQueue::adata.push_front(value);
}
void push_back(T value)
{
VectorQueue::push_back(value);
}
//repeat it fo every virtual method of BaseQueue class (interface)
};
But it's awful.
push_back from BaseQueue isn't implemented on the BaseDeque side of the inheritance chain, and thus the childmost class is still abstract.
I think you're trying to force a class relationship here that shouldn't exist. Note how in the standard library deque and vector are distinct container types and things like queue adapt those containers to very precise interfaces rather than trying to inherit.
Even if you solve your diamond issue (or follow #Mark B's advice and keep them separate), you have a few other issues in there:
template <typename T>
class VectorQueue: public BaseQueue<T>
{
typedef typename std::vector<T> array;
private: array adata; // if this is private, VectorDeque can't reach it
public:
// constructors have an initializer section
// member variables should be initialized there, not in the body
VectorQueue()
// : adata() // however, no need to explicitly call default constructor
{
// adata = array();
}
};
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>
{
void push_front(T value)
{
// if adata is protected, you can just access it. No need for scoping
/*VectorQueue::*/ adata.push_front(value);
// Error: std::vector doesn't have a method push_front.
// Perhaps you meant to use std::list?
}
};
Multiple inheritance and static polymorphism are of help, for instance:
// Abstract bases
template <typename T, typename Val>
class BaseQueue
{
public :
void push_back(Val val)
{
static_cast<T*>(this)->push_back(val);
}
// ...
};
template <typename T, typename Val>
class BaseDeque
{
public:
void push_front(Val val)
{
static_cast<T*>(this)->push_front(val);
}
// ...
};
// Concrete class
#include <deque>
template <typename Val>
class QueueDeque:
public BaseQueue<QueueDeque<Val>, Val>,
public BaseDeque<QueueDeque<Val>, Val>
{
std::deque<Val> vals;
public:
void push_front(Val val)
{
vals.push_front(val);
}
void push_back(Val val)
{
vals.push_back(val);
}
// etc..
};
int main()
{
QueueDeque<int> vd;// no more error
vd.push_front(5);
vd.push_back(0);
return 0;
}