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
Related
I have encountered an incomplete type error in designing my library's CRTP inheritance structure. I am cognizant of the fact that this might possibly be solved by way of type traits but I do not even know where to begin and would like some guidance in that regard.
Roughly speaking, Alg and Geo are two interfaces that inherit from a shared base. Alg is fairly straightforward but Geo is a bit more complex in that it takes as a template parameter a class deriving from Alg. The incomplete type error is in reference to this functionality. Minimal reproducible example
The following skeleton illustrates the problem at hand.
template <typename Derived>
struct FitCRTP {
Derived& derived() { return static_cast<Derived&>(*this); }
Derived const& derived() const { return static_cast<Derived const&>(*this); }
};
template <typename Derived>
class FitBase : public FitCRTP<Derived> {
friend class FitCRTP<Derived>;
protected:
FitBase() {}
explicit FitBase(const Matrix& data) {
this->derived().fit(data);
}
};
template <typename Derived>
class Alg : public FitBase<Derived> {
friend class FitBase<Derived>;
public:
void fit (const Matrix& data) {
this->derived().compute(data);
}
protected:
// constructors and misc functions ...
}
};
template <typename Derived, class A,
class = std::enable_if_t<std::is_base_of_v<Alg<A>, A>>>
class Geo : public FitBase<Geo<Derived, A>> {
friend class FitBase<Geo<Derived, A>>;
public:
void fit (const Matrix& data) {
this->derived().compute(data, A(data).getGuess()); // <-- Problem line!
}
protected:
// constructors and misc functions ...
}
};
Below is an example of one of A's derived classes and a usage example.
template <class A>
class SpathSVD : public Geo<SpathSVD<A>, A> {
friend class Geo<SpathSVD<A>, A>;
typedef Geo<SpathSVD<A>, A> Base;
public:
SpathSVD<A> : Base() {}
SpathSVD<A> (const Matrix& data) : Base(data) {}
protected:
SpathSVD& compute (const Matrix& data, Guess g) {
// Algorithm implementation ...
}
}
int main() {
SpathSVD<SomeAlgVariant> S;
S.fit(data);
}
The error occurs because the compute function cannot be found in Geo<SpathSVD<SomeAlgVariant>, SomeAlgVariant>. How can I fix this?
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;};
};
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;
Well this has been giving me grief...
#include <iostream>
class InterfaceClass
{
public:
void test()
{
std::cout<<"Hello there.\n";
}
};
template <class T>
class TemplateClass
{
public:
T t;
};
class TestClass: public InterfaceClass
{};
class TestInheritor
{
public:
TemplateClass < InterfaceClass >* templateInherit;
InterfaceClass* normalInherit;
void test()
{
normalInherit->test();
templateInherit->t.test();
}
};
int main (int nargs, char ** arg)
{
TestInheritor ti;
ti.normalInherit = new TestClass; // THIS ONE COMPILES OKAY.
//ti.templateInherit = new TemplateClass <TestClass>; // COMPILE ERROR.
// THIS WORKS THOUGH
TemplateClass <TestClass> * tempClass = new TemplateClass <TestClass>;
ti.templateInherit=(TemplateClass <InterfaceClass>*)tempClass; // WHY DO I HAVE TO EXPLICITLY CAST?
// OUTPUT WORKS AS EXPECTED.
ti.test();
return 0;
}
The normal inheritance example works just fine. The TestClass is automatically converted to a InterfaceClass. However, with the template example, it gives a compile error:
error: cannot convert 'TemplateClass<TestClass>*' to 'TemplateClass<InterfaceClass>*' in assignment
In my mind, it is obvious that you can convert TemplateClass<TestClass>* to TemplateClass<InterfaceClass>*... So what am I missing here?
I can fix it by explicitly casting the template class to the base class, I am able to use the inherited test() function without any problem... So why am I required to explicitly cast the template class?
Sorry if that's confusing... It's hard for me to explain this problem.
Okay, I understand the issue a little more. I have decided to add a template to TestInheritor like so:
template <class T2>
class TestInheritor
{
public:
TemplateClass < T2 >* templateInherit;
InterfaceClass* normalInherit;
void test()
{
normalInherit->test();
templateInherit->t.test();
}
};
int main (int nargs, char ** arg)
{
TestInheritor <TestClass> ti;
ti.normalInherit = new TestClass;
ti.templateInherit = new TemplateClass <TestClass>;
ti.test();
return 0;
}
Probably not the perfect solution, but it works for my purposes.
Ah, and I see your solution:
#include <iostream>
class InterfaceClass
{
public:
void test()
{
std::cout<<"Hello there.\n";
}
};
class TestClass: public InterfaceClass
{};
template <class T>
class TemplateClass
{
public:
T t;
};
template<>
class TemplateClass<TestClass> : public TemplateClass<InterfaceClass>
{
public:
};
class TestInheritor
{
public:
TemplateClass < InterfaceClass >* templateInherit;
InterfaceClass* normalInherit;
void test()
{
normalInherit->test();
templateInherit->t.test();
}
};
int main (int nargs, char ** arg)
{
TestInheritor ti;
ti.normalInherit = new TestClass;
ti.templateInherit = new TemplateClass <TestClass>;
ti.test();
return 0;
}
Consider
class Base
{};
class Derived : public Base
{};
template<typename T>
class TemplateClass
{};
Contrary to what you might think, TemplateClass<Derived> is not a TemplateClass<Base> in the sence of inheritance, so pointers to the former can't be implicitly converted to pointers of the later.
So why does explicitly casting a pointer to TemplateClass<Derived> to a pointer of TemplateClass<Base> compile? Because any pointer of a certain type may be explicitly cast to any pointer of any other type, however there are no guarantees the conversion is valid! You could for instance just as well write
int* i = (int*) new TemplateClass<Derived>;
and it would compile just fine, even though it is clearly an invalid conversion.
Now why does your example work? Sheer luck. At the point a pointer containing an address obtained through an invalid pointer cast gets dereferenced, the program becomes invalid and its bahavior undefined. Anything can happen, including doing what you would expect.
If you want TemplateClass<Derived> to be a TemplateClass<Base> in the sence of inheritance, you can define an specialization of TemplateClass<> stating this relationship explicitly, like the following:
template<>
class TemplateClass<Derived> : public TemplateClass<Base>
{};
I suspect I can't do this directly using a PIMPL pattern. Is it possible to have a smart pointer to a template class? I have not been able to compile by turning knobs on the shared_ptr declaration.
// ============Foo.h ============
// Forward declare the implementation
template <typename T> class FooImpl;
class Foo
{
public:
Foo getInstance(const string& fooType);
...
private:
shared_ptr< FooImpl<T> > m_impl;
};
// ============FooImpl.h ============
template <typename T>
class FooImpl
{
...
};
Under Visual Studio 2008: "error C2065: 'T' : undeclared identifier". I receive a similar error under GCC. If I un-parameterize FooImpl (so that FooTempl inherits from FooImpl), the code will compile.
I suspect I can't paramaterize the smart pointer, but I could be wrong.
EDIT: The second Visual Studio error is more telling: "error C3203: 'FooImpl' : unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type"
Jeff
I'm not entirely certain what you are trying to accomplish, but does this help?
Try 1:
// ============Foo.h ============
// Forward declare the implementation
template <typename T> class FooImpl;
template<class C>
class Foo
{
public:
Foo getInstance(const string& fooType);
...
private:
shared_ptr< FooImpl<C> > m_impl;
};
// ============FooImpl.h ============
template <typename T>
class FooImpl
{
...
};
Try 2:
// ============Foo.h ============
// Forward declare the implementation
class FooImplBase;
class Foo
{
public:
Foo getInstance(const string& fooType);
...
private:
shared_ptr< FooImplBase > m_impl;
};
// ============FooImpl.h ============
class FooImplBase {
public:
virtual void AnAPI();
virtual int AnotherAPI();
};
template <typename T>
class FooImpl : public FooImplBase
{
...
};
The code you have posted cannot compile since T does not mean anything in the context of Foo. The compiler expects a type called T here which does not exist there... Not entirely sure what you are trying to accomplish, but wouldn't the following solve your problem?
// ============Foo.h ============
class FooImplBase {
virtual void WhateverFooImplIsSupposedToDo() = 0;
};
template <typename T> class FooImpl : public FooImplBase {
T mInstance;
public:
FooImpl(T const & pInstance) : mInstance(pInstance) {}
virtual void WhateverFooImplIsSupposedToDo()
{
// implementation which deals with instances of T
}
};
class Foo
{
public:
Foo getInstance(const string& fooType) {
// use m_impl->WhateverFooImplIsSupposedToDo...
}
template < class T >
Foo( T const & pInstance ) : m_impl(new FooImpl<T>(pInstance)) {}
private:
shared_ptr< FooImplBase > m_impl;
};
You're doing it right, just make sure T is defined. This compiles for me on MSVC++ 2010:
#include <memory>
using namespace std;
template<class T>
class Blah {
public:
Blah() { }
};
class Foo {
public:
shared_ptr<Blah<int>> ptr;
Foo() : ptr(new Blah<int>()) { }
};
If you're on an older compiler that hasn't incorporated this feature of C++11 yet, change
shared_ptr<Blah<int>> ptr;
To
shared_ptr<Blah<int> > ptr;
So the compiler doesn't think the >> is a right shift. C++11 doesn't have this problem though.
I don't know in advance I am going to have a Blah, only a Blah.
From the language point of view, Blah<T> is meaningless because T doesn't exist. Depending on what you're exactly trying to do, you can
make Foo a template, too, so that you can declare a template parameter T:
template<typename T>
class Foo
{
public:
Foo getInstance(const string& fooType);
...
private:
shared_ptr< FooImpl<T> > m_impl;
};
which 'fixes' the choice of T when you declare a variable of type Foo<T>;
or make FooImpl explicitly derive from a common base:
class FooBase {
// need to define the interface here
};
// this is a class definition whereas previously you only needed a declaration
template<typename T>
class FooImpl: public FooBase {
// definition here
};
class Foo
{
public:
Foo getInstance(const string& fooType);
// we needed the definition of FooImpl for this member
// in addition this member is quite obviously a template
template<typename T>
void
set(FooImpl<T> const& foo)
{
m_impl.reset(new FooImpl<T>(foo));
}
// not a member template!
void
use()
{
// any use of m_impl will be through the FooBase interface
}
private:
shared_ptr<FooBase> m_impl;
};
where for a given Foo instance any kind of FooImpl<T> can be set dynamically and then used through the FooBase interface. This is a kind of type erasure as it's called in the C++ world.
We can use templates to write a generic smart pointer class. Following C++ code demonstrates the same. We don't need to call delete 'ptr', when the object 'ptr' goes out of scope, destructor for it is automatically.
#include<iostream>
using namespace std;
// A generic smart pointer class
template <class T>
class SmartPtr
{
T *ptr; // Actual pointer
public:
// Constructor
explicit SmartPtr(T *p = NULL) { ptr = p; }
// Destructor
~SmartPtr() {
cout <<"Destructor called" << endl;
delete(ptr);
}
// Overloading dereferncing operator
T & operator * () { return *ptr; }
// Overloding arrow operator so that members of T can be accessed
// like a pointer (useful if T represents a class or struct or
// union type)
T * operator -> () { return ptr; }
};
int main()
{
SmartPtr<int> ptr(new int()); // Here we can create any data type pointer just like 'int'
*ptr = 20;
cout << *ptr;
return 0;
}
out put:
20
Destructor called