I have a templated class where I ensure the type parameter is a subclass of some abstract base class like this:
#include <type_traits>
template<typename T>
class Foo
{
static_assert(
std::is_base_of<MyBase, T>::value,
"T must be a descendant of MyBase"
);
void SomeMethod()
{
auto bar = T();
//or
auto bar = T("Constructor with parameter")
bar.someFunctionOnMyBase();
}
};
Now in programming languages like C# or Java I can use this information and use he type information to call methods on the template type. Is such thing possible? Bonus points if it also possible to call constructors with the correct parameters.
Yes, this is totally fine (as long as both the constructor that you intend to call and the destructor of class T are publicly accessible).
As a matter of fact you don't even need to have the static_assert, and as long as a member function T::someFunctionOfMyBase exists (even if it isn't the one defined in MyBase and just happens to share the name), this will still compile.
Related
I want to have classes with a static data member knowing the class's complete size. This is for storing singleton instances, in case you want to know the actual use case of this.
In my naive implementation of this feature, I wanted to use a mixin class to add the special data member to my class. The mixin class would have to know the complete class (in order to know the complete class's size), so I implement it using the Curiously Recurring Template Pattern, a little bit like this:
template<class ObjectType>
class SingletonOf
{
static inline /* some type same size as ObjectType */ instance_memory;
public:
void *operator new(std::size_t)
{
return &instance_memory;
}
void operator delete(void *)
{
}
};
class foo : public SingletonOf<foo> // CRTP used here, to let SingletonOf know foo
{
// foo data members...
// foo member functions...
};
void bar() {
foo *p = new foo; // calls SingletonOf<foo>::operator new and returns the instance memory
}
Cute, right? Well, I learned that the following in C++20 is ill-formed (note: in all the code samples below, the class foo and the function bar() do not change. Also I will not keep writing the empty definition of SingletonOf::operator delete, because you can remember that it's there):
template<class ObjectType>
class SingletonOf
{
static char inline instance_memory[sizeof(ObjectType)]; // syntax error: incomplete type
public:
void *operator new(std::size_t) { return instance_memory; }
...
Now, we will all agree the reason why that is ill-formed - and I am not complaining, just informing - is that ObjectType is foo, and until the closing brace of foo, foo is an incomplete type. And, obviously, sizeof cannot be called on incomplete types. So, I am fine with that. However, the following using a nested class-template does work - at least according to clang++ in c++20 mode, I think?
template<class ObjectType>
class SingletonOf
{
template<class CompleteObjectType>
struct InstanceMemory
{
static char inline instance_memory[sizeof(CompleteObjectType)];
};
public:
void *operator new(std::size_t) {
return InstanceMemory<ObjectType>::instance_memory;
}
...
Now my question is: why does that work? Or, let's start with the more fundamental question: does that work, actually? As of this writing, just to be clear, I have not verified that bar() actually calls the intended operator new and returns the foo-sized instance memory. Probably, should do that. But I'm busy. What I do know at this time, is that my clang++ in c++20 mode compiles it. This compilation includes compiling the function bar(), which allows me to be certain it instantiates the template. So that is to back up my contention that the compiler is accepting it. There are no errors or warnings give, just an output object file.
If I am right that this second code is well-formed, then it looks like ObjectType (= foo) in the body of operator new in the second code sample, is considered a complete type. How did that happen?
This isn’t really any different from having InstanceMemory defined in a namespace: until it is instantiated, its template argument need not be complete. This separation works because it removes the presumption that you should be able to use decltype(SingletonOf::instance_memory) immediately after declaring it.
When SingletonOf<ObjectType> is being instantiated, ObjectType is incomplete. That's why you can't get the size of it.
However, the member function bodies of SingletonOf work as if they are placed just after the type. And those functions get instantiated at a point when ObjectType is complete. This is why ObjectType is complete and visible to member functions of SingletonOf<ObjectType>.
Your inner struct InstanceMemory is itself a template. And you instantiate it within a member function of the outer template. Since that member function sees ObjectType as complete, so too does InstanceMemory<ObjectType>.
All you have to do is make sure to instantiate InstanceMemory<ObjectType> at a point where ObjectType is complete.
In the code below, I get a compiler error when trying to push fooBaz onto v. This surpises me since Baz is a derived class of Bar.
Why is this not allowed, and what can I do if want to put several Foo instances, templated on classes derived from the same base class, into a vector?
#include <iostream>
#include <vector>
template<typename T>
class Foo {};
struct Bar {};
struct Baz : public Bar {};
int main() {
Foo<Bar> fooBar;
Foo<Baz> fooBaz;
std::vector<Foo<Bar>> v;
v.push_back(fooBar);
v.push_back(fooBaz);
return 0;
}
Java Generics are not the same kind of thing as C++ templates.
C++ values of class type are not the same thing as Java reference variables of class type.
You are running into both problems here.
C++ templates generate a new, unrelated type for each set of template arguments. You can create a common base, but you have to do it yourself.
Java Generics, under the hood, actually create a single class. It then writes casting operations at inputs and outputs.
So a Java Generic, Foo<Base> and Foo<Derived> are related, because the the Java Generic actually creates a Foo<Object> then wraps it up in casts, and those casts in Foo<Base> and Foo<Derived> are compatible. (well, not always Object, you mark up the generic arguments with information that Java uses to determine what the actual type it writes its Generic for, but that gives you the idea).
In C++, there is no relation. (well, template pattern matching gives you a compile-time relation, but no runtime relation at all)
The second problem is that you are treating values of class type like references. In C++, a Foo is an actual foo. It represents a block of memory that is an instance of that class. In Java, a Foo is a smart pointer to an object on the heap somewhere that obeys the Foo protocol (is a derived class).
You cannot easily make a value of type Foo in Java, and you cannot easily make a mark and sweep smart pointer to a Foo in C++.
Foo<Bar> fooBar;
Foo<Baz> fooBaz;
these are two unrelated types. They are stored on the stack (automatic storage).
std::vector<Foo<Bar>> v;
This stores a buffer of memory containing Foo<Bar> objects packed together.
v.push_back(fooBar);
This copies a fooBar instance from automatic storage into the vector.
v.push_back(fooBaz);
This doesn't work, because fooBar and fooBaz are unrelated types.
Now, prior to c++23 reflection, mimicing what Java does is difficult in C++. You have to do some steps manually.
First, instruct Foo to understand inheritance when told so manually:
struct empty_t {};
template<class T, class Base=empty_t>
class Foo:Foo<Base> {};
template<>
class Foo<empty_t, empty_t> {
virtual ~Foo() {}
};
struct Bar {};
struct Baz : public Bar {};
auto fooBar = std::make_unique<Foo<Bar>>();
auto fooBaz = std::make_unique<Foo<Baz, Bar>>();
std::vector<std::unique_ptr<Foo<Bar>>> v;
v.push_back(std::move(fooBar));
v.push_back(std::move(fooBaz));
this compiles.
In c++23 compile time reflection should let you auto-detect the base classes of Baz and have Foo<Baz> automatically inherit from Foo<Bases>... if you want.
Now, inheritance is only one kind of way to handle polymorphism in C++, but I think is enough for today.
As mentioned in a comment, templates are like recipes. You need to instantiate a class template to get a class, before it is just a template. There is no implicit relation between different instantiations (other than being instantiations of the same template). Maybe this gets more clear when you consider following example:
template<typename T> struct Foo {};
template <> struct Foo<int> { void bar(){} };
template <> struct Foo<double> { void moo(){} };
int main() {
Foo<int> x;
x.bar();
Foo<double> y;
y.moo();
}
Foo<int> and Foo<double> are two unrelated types with completely different methods. If they were not instantiations of a template, but "ordinary" types with different members you would not be surprised that you cannot push a FooA into a std::vector<FooB>.
A std::vector<Foo<Bar>> can only hold elements of type Foo<Bar>. The vector is not aware that this type is the result of instantiating the template Foo. And even if it was, you cannot assign a Foo<Bar> to a Foo<Baz> unless you provide a conversion.
Actually there is more to your misunderstanding that needs to be debunked. Suppose you have a std::vector<Bar> then you also cannot push a Baz to that vector. See here for why not: What is object slicing?. And here for what to do instead: https://stackoverflow.com/a/16126649/4117728.
I am a beginner in STL. I'm trying to code a toystl to learn STL. When I code about iterator, I'm puzzled if I should code a simple auto_ptr first and inherint from it.
I wrote a base class called iterator. And now it works like this,
struct iterator{};
template <class T>
struct vector_itorater: public toystl::iterator<toystl::random_access_iterator_tag, T>{};
If i need another base class works like a "auto_ptr"? just like this
// firstly define a sort of auto_ptr as base class
struct auto_ptr{};
// secondly inherint from auto_ptr
template <class T>
struct vector_itorater: public auto_ptr{};
Does this work? Or does STL do it like this?
I think you mixed up runtime polymorphy and compile time polymorphy. When the compiler instantiates a template, it cares about its visible interface of the concrete object. It does not care if this object has a inheritance relationship with other classes, it will pass as long as the concrete object can be used within the concrete context.
template <class C>
void foo(const C& bar)
{
// at the time of writing we don't know anything of C,
// only that it has a callable baz member (either a
// member function or a member with a call operator).
// This works, since the compiler knows the exact type
// during template instantiation, but we don't have to
// care in advance.
bar.baz();
}
struct X
{
void baz() const;
};
void grml()
{
X x;
// The compiler fills in X as the template type
// parameter for us. So the compiler creates a
// void foo<X>(const X&) function for us.
foo(x);
}
In this example when the compiler sees the template, it has no clue how this template will be called later. Only once the template gets instantiated (used), the compiler then will check if the passed type is suitable for this template.
Here it is not needed to have a common base class to derive every possible implementation from. The STL uses templates in order to avoid to use such base classes, since they give you a burden on your design later, and if you have virtual members in the base to override, you can get a serious performance penalty.
I would like to understand if it is possible to use a nested class of a leaf CRTP class in the base CRTP class. The example below demonstrates the problem.
#include <iostream>
using namespace std;
template<class T>
class A
{
protected:
T* asLeaf(void)
{return static_cast<T*>(this);}
T const* asLeaf(void) const
{return static_cast<T const*>(this);}
public:
struct Inner
{int a = 10;};
void dispInner(void) const
{std::cout << asLeaf()->inner.a << std::endl;}
// I would like to use T::Inner in this class, e.g.
// typename T::Inner mvInnerA;
// However, I understand that it is not possible to
// use it in the form that is stated above. Thus,
// I am looking for any possible workarounds.
};
class B: public A<B>
{
public:
struct Inner: public A<B>::Inner
{int b = 20;};
protected:
friend A<B>;
B::Inner inner;
public:
void dispInner(void) const
{
A<B>::dispInner();
std::cout << asLeaf()->inner.b << std::endl;
}
};
int main()
{
B b;
b.dispInner();
return 0;
}
EDIT
I would like to provide several further comments based on the feedback that I have received:
I am aware that I may not be using adequate design practices. In particular, it may be questioned whether A should be aware of the existence of inner. However, I would like to define an object inner of the type B::Inner in A instead of providing the definition of inner in B and using it in A.
I am aware that I cannot forward declare B and/or B::Inner and of the reasons why this cannot be done. Thus, technically, the design problem does not have a solution. However, I am looking for a feasible workaround.
I have already considered several alternative solutions:
One of the possible feasible solutions is not to make attempts to 'define' B::Inner inner in A and use the member functions of A to provide the functionality that allows to modify the A<B>::Inner part of B::Inner inner.
Another possible solution is to define the classes A<B>::Inner and B::Inner explicitly (i.e. not as nested classes). However, I would prefer to avoid this, because, by design, it is not expected that any classes that do not derive from A will need to interact with A<B>::Inner or the classes that derive from A<B>::Inner
Both solutions that I have presented may be acceptable. However, I am looking for any feasible alternatives.
The standard say that:
A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.
It follows that B isn't a completely-defined object when you specialize A as A<B>. Therefore you can't expect to be able to access its members or types or whatever from within the definition of A (even though you can call back the derived class from within the definition of a member method of A, that is perfectly legal instead other than the purpose of the CRTP idiom).
In other terms, when you do this:
typename T::Inner mvInnerA
You have no guarantees that T is a completely-defined object and that's why you get the error.
A few alternatives:
You can define mvInnerType as a function instead of as a type and use it as a factory to create objects of type T::inner:
[static] auto mvInnerA() {
return typename T::Inner{};
}
Use it either as:
auto foo = A<B>::mvInnerA();
Or:
auto foo = obj.mvInnerA();
The right form depends on the fact that you make it static or not.
Note that you can still use the hidden type somehow, even if its name isn't accessible:
using HiddenType = decltype(A<B>::mvInnerA());
HiddenType foo = A<B>::mvInnerA();
You can define mvInnerA using a template a alias declaration like this:
template<typename U = T>
using mvInnerA = typename U::Inner;
Then use it as:
auto foo = A<B>::mvInnerA<>{};
For the type T is (let me say) indirectly used through U only when mvInnerA is instantiated, you don't have the problem mentioned above. The price to pay for that is the presence of an annoying <> and the fact that one can pass a custom type to mvInnerA.
How you can use an inner type of a CRTP template parameter is severely limited.
There can be no use in the scope of the class template definition itself. When instantiating the template, it will require the type B to be fully defined, which like skypjack points out, it isn't. You can however use it in contexts that aren't immediately instantiated with the class template, which is mostly the member functions of A.
And while you cannot have a type alias for B::Inner, you can have a type alias template
template<class C>
using Inner = typename C::Inner
Which A's member functions can use to avoid the verbosity of typename B::Inner and instead use Inner<B>.
I have the following problem with inheritance and templates:
class Base {};
class Deriv : public Base {};
template <class T> class X{};
void f(X<Base>& inst) {}
int main()
{
X<Base> xb;
f(xb);
X<Deriv> xd;
f(xd);
return 0;
}
The program doesn't compile because there is not relation between X<Base> and X<Deriv>. Nevertheless I think it should be possible to do everything that can be done with X<Base> also with X<Deriv>. Is there anything that I could do other than copying the function body of f to a new function void g(X<Deriv>& inst)?
You could just continue using templates:
template<class T>
void f(X<T>& inst) {}
will work for both X<Base> and X<Derived>.
The compiler might duplicate the code (if it is not smart enough), but you don't have to.
Why do you think they should be related? Consider the following:
template<typename T>
class X;
template<>
class X<Base> {
int x;
};
template<>
class X<Deriv> {
double d;
};
They're definitely not interchangeable. So no, there is no relation between those classes and you can't pass one to a function expecting the other. You'll have to do something like make both types inherit from another common type that exposes the interface you need.
Regarding your comment, you can use type traits and static_assert to do what you would do in Java:
template<typename T>
void f(X<T>& inst) {
static_assert(std::is_base_of(Base, T)::value, "Template type must subclass Base");
// body of function...
}
If you need such functionality, then you must template on the type- or overload, as you have said. Alternatively, you might explicitly specialize X such that X<Derived> : X<Base>.
Different instantiations of a template are unrelated types, even if the instantiating template arguments are related. That is, X<A> is not related to X<B> regardless of what the relationship between A and B might be.
Now as of what can be done, it depends on what your template actually is. In some cases you can provide conversions so that the X<Derived> can be converted to a X<Base> for a particular operation. Another alternative is modifying your function to be able to take any X<T> for which T derives from Base (this can be done by creating a template and using SFINAE to disallow calling it with Ts that don't derive from Base. Again, depending on what your template is, you might be able to offer access to the underlying type, in which case the function could take a reference to Base (consider shared_ptr or unique_ptr with the .get() method)
Without a description of what you actually want to get done it is impossible to provide a good alternative.
It depends. Consider the case where X is std::shared_ptr. It would break type safety if std::shared_ptr<Derived> was derived from std::shared_ptr<Base>, but instead there is an implicit value conversion.
However, since you’re passing by reference to non-const, such a value conversion will not help you you directly.
Other possibilities include inheriting from a common interface, and templating your function.