My question is how to make lifetime extension work with CRTP. For example, the following code is perfectly valid:
struct A {
const int& ref;
};
struct B {
const A& a;
};
int main() {
B b{{123}};
return b.a.ref;
}
Its CRTPed version is not:
template <class DerivedT>
class Gettable {
public:
int Get() const {
return static_cast<const DerivedT*>(this)->GetImpl();
}
};
class A : public Gettable<A> {
friend class Gettable<A>;
public:
A(int r) : ref{r}{}
private:
int GetImpl() const {
return ref;
}
const int& ref;
};
template <class T>
class B {
public:
B(const Gettable<T>& gettable) : get_{gettable}{}
int DifferentGet() const {
return get_.Get();
}
private:
const Gettable<T>& get_;
};
int main() {
B b{A{123}};
return b.DifferentGet();
}
The problem is that the original A and its Gettable<A> subobject only exist till the the of B constructor.
I have two questions:
1) Why? It is in no way different from the first case of structs, every lifetime is known at compile time, so I believe that compiler should be able to extend lifetime of all temporaries.
2) Is there any good way to overcome this issue?
1) Why?
Because there is a function involved - the constructor. The temporary is not bound directly to the member, but rather it is bound directly to the argument of the function - whose lifetime extends until the end of the function, which does not extend beyond the full expression that invoces the function.
It is in no way different from the first case of structs
It is different. There is no constructor involved in aggregate initialisation. In that case, the compiler knows the lifetime of the member, and it knows that the member is initialised with the temporary. The lifetime extension rule applies.
so I believe that compiler should be able to extend lifetime of all temporaries.
Consider following example:
struct foo {};
struct bar {
bar(const foo& farg);
const foo& fmem;
};
bar b({});
Should the lifetime of the temporary to extend for the lifetime of b? The standard says, that it doesn't. You appear to be arguing that it should.
Consider following possible implementations of the constructor:
bar::bar(const foo& farg) : fmem{farg} {} // 1
foo fanother;
bar::bar(const foo& farg) : fmem{fanother} {} // 2
If the implementation happens to be 1, then you guessed right, the life time extension is needed. If implementation is 2, then we are unnecessarily extending a temporary that is not referred to anymore.
The language designers chose to not extend such temporary, probably so that life times of temporaries don't get extended unnecessarily. As a consequence, implementation 1 is wrong, as well as your CRTP example.
Concisely: The compiler can only extend the lifetime of a temporary until the lifetime of the reference to which the temporary is bound directly. The compiler cannot know what will be done with the reference within the function. It cannot know that an argument has something to do with a member. Those are only known when the constructor is compiled - not when a call to the constructor is compiled.
2) Is there any good way to overcome this issue?
Use either int* or std::reference_wrapper<int> as the constructor argument. Former is more concise, but latter has convenient property of not having a null representation. These should make it harder to accidentally bind a dangling reference. Regardless, document carefully that the referred object must still be valid when Get is called.
I believe that the most general solution is something like this. This way it works even for multilevel inheritance.
#include <iostream>
#include <utility>
#include <type_traits>
struct NullType {};
// Helper class for casting
template <class Derived>
class DerivedCaster {
protected:
Derived* GetDerived() {
return static_cast<Derived*>(this);
}
const Derived* GetDerived() const {
return static_cast<const Derived*>(this);
}
};
// Matches the predicate against the types and remembers the first
// satisfying argument
template <template <class T> class Predicate, class... Args>
struct FindFirstMatching {
using Type = ... ; // default NullType
static const bool has_match = ... ;
};
// Structure which gets the deepest class from CRTP inheritance chain
// by looking at the instantiated parent class template
template<typename T>
struct GetDeepest
{
using Type = T;
};
template<template<class...> class DT, class... T>
struct GetDeepest<DT<T...>>
{
template <class CLS>
struct Predicate {
static const bool value = std::is_base_of<DT<T...>, CLS>::value;
};
static const bool HasCRTPDerived = FindFirstMatching<Predicate, T...>::has_match;
using DerivedT = typename FindFirstMatching<Predicate, T...>::Type;
using Type = std::conditional_t<HasCRTPDerived, typename GetDeepest<DerivedT>::Type, DT<T...>>;
};
// First abstract class
template <class DerivedT>
class Gettable : public DerivedCaster<DerivedT> {
public:
int Get() const {
return DerivedCaster<DerivedT>::GetDerived()->GetImpl();
}
};
// Second abstract class
template <class DerivedT>
class Incrementable : public DerivedCaster<DerivedT>,
public Gettable<Incrementable<DerivedT>> {
friend class Gettable<Incrementable<DerivedT>>;
public:
int Increment() const {
return ++(this->Get());
}
private:
int GetImpl() const {
return DerivedCaster<DerivedT>::GetDerived()->GetImpl() + 100;
}
};
// non-abstract class
class A : public Incrementable<A> {
friend class Incrementable<A>;
public:
A(int r) : ref_{r}{}
private:
int GetImpl() const {
return ref_;
}
int ref_;
};
// Helper to get the copy of the underlying non-abstract class
template <class T>
auto GetDeepestLevelCopy(const T& arg) {
return static_cast<const typename GetDeepest<T>::Type&>(arg);
}
// Some other class which wants a copy
template <class T>
class B {
public:
B(const Gettable<T>& gettable) : get_{GetDeepestLevelCopy(gettable)}{}
int DifferentGet() const {
return get_.Get();
}
private:
typename GetDeepest<Gettable<T>>::Type get_;
};
int main() {
static_assert(std::is_same_v<GetDeepest<Gettable<Incrementable<A>>>::Type, A>);
static_assert(std::is_same_v<decltype(GetDeepestLevelCopy(std::declval<Gettable<Incrementable<A>>>())), A>);
B b{A{123}};
std::cout << b.DifferentGet() << "\n";
// prints 223
return 0;
}
This looks monstrous, but I don't know whether there is a better solution.
Related
I got an example code as below:
#include <iostream>
template<class T1>
class B {
public:
B() : t1_(*this) {}
void Test() {
t1_.Test();
}
void Print() const {
std::cout << "test\n";
}
private:
T1 t1_;
};
template<template<class> class TB>
class A1 {
public:
explicit A1(const TB<A1<TB>> &b) : b_(b) {}
void Test() {
b_.Print();
}
private:
const TB<A1<TB>> &b_;
};
int main() {
B<A1<B>> bt;
bt.Test();
}
This is insight by this answer, this code make sure a class B has a member A1 and the A1 has a reference of B.
Although this code works, I really don't know how it works, especially the code const TB<A1<TB>> &b_;. Since the TB is a template template parameter, The TB<...> is a specialization of TB, which parameter is A1<TB>, right? Then what does the second TB in TB<A1<TB>> mean? If the second TB is a template, why there's no parameter?
As Matthieu Brucher mentioned, this code is indeed used to avoid an infinite recursion. Since I'm not fully understand how this code works, can any body explain how a compiler do to make this code work? Or, what should this code look like after it is being compiled?
The second TB doesn't have a parameter because of A1 declaration:
template<template<class> class TB>
class A1;
This says that A1 takes one template argument, and the parameter itself takes an unspecified template argument. A1 will do whatever it pleases with this template argument, but it must not be given when declaring A1, and this breaks the infinite recursion that would arise without this facility.
So for instance, you can write:
A1<TB> foo;
You can also write:
A1<std::vector> foo(std::vector<A1<std::vector>>()); // UB because of b storage, but it's the example
B expects a type, whereas A1 expects a template.
So you may have B<int> but not A1<int>.
Similar way, you may have A1<B> but not B<B>.
Back to
template<template <class > class TB> class A1;
TB is a template, not a type, but TB<int> is a type.
so for TB<A1<TB>>,
(inner) TB is a template.
A1<TB> is a type.
TB< T2 > is a type (with T2 = A1<TB>).
As inspired by Matthieu Brucher and Jarod42, I will try to use the view of a compiler to explain this, correct me if I'm wrong.
As Jarod42 mentioned:
(inner) TB is a template.
A1< TB > is a type.
TB< T2 > is a type (with T2 = A1)
and by the fact that template will instantiate when it is used, the line B<A1<B>> bt; is the place where the template fall into certain type.
So A1<B> is a type, let's make the real class named A1_IMPL, that is A1<B>--A1_IMPL, B<A1<B>> is a type, let make the class named B_IMPL, that is B<A1<B>>--B<A1_IMPL>--B_IMPL. So the B_IMPL looks like this:
class B_IMPL {
public:
B_IMPL() : t1_(*this) {}
void Test() {
t1_.Test();
}
void Print() const {
std::cout << "test\n";
}
private:
A1_IMPL t1_;
};
A1 will look like this:
class A1_IMPL {
public:
explicit A1_IMPL(const B<A1<B>> &b) : b_(b) {}
void Test() {
b_.Print();
}
private:
const B<A1<B>> &b_;
};
that's not finished, since A1<B>--A1_IMPL,B<A1<B>>--B<A1_IMPL>--B_IMPL, the final A1_IMPL will looks like this:
class A1_IMPL {
public:
explicit A1_IMPL(const B_IMPL &b) : b_(b) {}
void Test() {
b_.Print();
}
private:
const B_IMPL &b_;
};
there's no template any more.
I am trying to create an adapter of a custom collection MyArray<T>.
For simplicity, the adapter Adapter does only one thing : cast the return result of MyArray<T>::get.
(In real case, MyArray and Adapter are very complex database manipulators.)
Version 1
Here is the first version, it works. (demo)
#include <iostream>
using namespace std;
template<class T>class MyArray{
public: T* database[20];
public: T* get(int index){return database[index];} //<-important
public: void set(int index,T* t){database[index]=t;}
};
template<class T,class T2> class Adapter{
public: MyArray<T>* underlying;
public: void setUnderlying(MyArray<T>* pUnder){underlying=pUnder;}
public: T2* get(int index){return static_cast<T2*>(underlying->get(index));}
//^ "Adapter::get()" is encapsulating "MyArray::get()"
};
class B{};
class C:public B{};
class D:public C{};
int main() {
MyArray<B> bs;
bs.set(0,new C()); //some can be new D()
//About the Adapter<C>, user is the one who sure that "bs" elements are "C*"-castable.
Adapter<B,C> cs; //<-- #1 need improve
cs.setUnderlying(&bs); //<-- assign MyArray* to adapter
C* c=cs.get(0);
return 0;
}
Version 2
Then, I want to sacrifice performance for readability and convenience. (#1)
Objective: Reduce amounts of template parameters of from 2 (Adapter<B,C>) to 1 (Adapter<C>).
Here is my work so far. It is compilable but should crash in some cases:-
class MyArrayBase{ //<--- new class
public: virtual void* get(int index)=0;
};
template<class T>class MyArray : public MyArrayBase{
public: T* database[20];
public: T* get(int index){return database[index];}
public: void set(int index,T* t){database[index]=t;}
};
template<class T2> class Adapter{
public: MyArrayBase* underlying; //<--- more abstraction
public: void setUnderlying(MyArrayBase* pUnder){underlying=pUnder;}
public: T2* get(int index){return static_cast<T2*>(underlying->get(index));} //#wrong
};
class B{};
class C:public B{};
int main() {
MyArray<B> bs;
bs.set(0,new C());
Adapter<C> cs; //<--- Yes! 1 template argument.
cs.setUnderlying(&bs);
C* c=cs.get(0);
std::cout<<"hi"<<std::endl;
return 0;
}
The reason that it is wrong :-
At the #wrong, void* (underlying B*) is static_cast to C*.
Here is the demo shows that it is wrong. (print 0 instead of 5)
Question
How to improve my first version of code to make Adapter have less template parameter?
Criteria :-
Don't use function pointer.
I feel it is possible with function pointer or std::function, but it seems to be a hack.
I also want to know if it is possible without using it.
Overhead should not be (roughly) worse than a single virtual calling (v-table) as in Version 2.
A single instance of Adapter<C>::setUnderlying must be able to accept any MyArray<X>* when it makes sense to call static_cast<C*>(X*).
MyArray and Adapter are library class. It doesn't have any knowledge about the type T or T2.
For example, I can't replace void* in class MyArrayBase with B*.
Light criteria :-
I prefer a solution using virtual function.
It would be ideal if there is no virtual-cost, but I don't think it is possible.
You can use some kind of wrapper that wraps the container, typically:
// Here T = T2, you want a virtual function that already give you the right type
template <typename T>
class Wrapper {
public:
virtual T* get(int index) const = 0;
};
// The real wrapper: It can give you T2 (To) but keep information
// about the original type since it is templated on Container
template <class To, class Container>
class WrapperContainer: public Wrapper<To> {
Container *cont_;
public:
WrapperContainer(Container *cont) : cont_(cont) { }
virtual To* get(int index) const override {
return static_cast<To*>(cont_->get(index));
}
};
The wrapper is the middle-guy between your Adapter that only knows the To type (the type you want to convert to) and your MyArray that only knows the From type (the type you want to convert from) - The WrapperContainer knows both, so it can safely convert from one to the other when it is possible.
The final Adapter:
template<class T2>
class Adapter {
std::unique_ptr<Wrapper<T2>> w_;
public:
template <typename Container>
void setUnderlying(Container *cont) {
w_ = std::unique_ptr<Wrapper<T2>>(new WrapperContainer<T2, Container>(cont));
}
T2* get(int index) {
return w_->get(index);
}
};
Using this you do not want a base class for MyArray since you need setUnderlying to deduce the type B from MyArray<B>:
// No more need for a base class
template<class T>
class MyArray {
T* database[20];
public:
T* get(int index){return database[index];}
void set(int index,T* t){database[index]=t;}
};
The important change from your code is actually this line:
return static_cast<To*>(cont_->get(index));
The type of cont_->get(index) is B* (in this example) and not void*, which makes the conversion work. This also prevents using setUnderlying with array of non-compatible type (try to uncomment the line cs.setUnderlying(&as); in the code below).
You can test it here: http://coliru.stacked-crooked.com/a/116305ec5f18b673
Suppose I have two classes...
We can call the first FooReader and it looks something like this:
class FooReader {
public:
FooReader(const Foo* const foo)
: m_foo(foo) {
}
FooData readFooDataAndAdvance() {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
return m_foo[m_offset++];
}
private:
const Foo* const m_foo;
size_t m_offset = 0; // used in readFooDataAndAdvance
};
We can call the second FooWriter and it looks something like this:
class FooWriter {
public:
FooWriter(Foo* const foo)
: m_foo(foo) {
}
void writeFooDataAndAdvance(const FooData& foodata) {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
m_foo[m_offset++] = foodata;
}
private:
Foo* const m_foo;
size_t m_offset = 0;
};
These both work wonderfully and do their job as intended. Now suppose I want to create a FooReaderWriter class. Note that the
I naturally want to say that this new class "is a" FooReader and "is a" FooWriter; the interface is simply the amalgamation of the two classes and the semantics remain the same. I don't want to reimplement perfectly good member functions.
One could model this relationship using inheritance like so:
class FooReaderWriter : public FooReader, public FooWriter { };
This is nice because I get the shared interface, I get the implementation and I nicely model the relationship between the classes. However there are problems:
The Foo* member is duplicated in the base classes. This is a waste of memory.
The m_offset member is separate for each base type, but they need to share it (i.e. calling either readFooDataAndAdvance and writeFooDataAndAdvance should advance the same m_offset member).
I can't use the PIMPL pattern and store m_foo and m_offset in there, because I'd lose the const-ness of the m_foo pointer in the base FooReader class.
Is there anything else I can do to resolve these issues, without reimplementing the functionality contained within those classes?
This seems ready made for the mixin pattern. We have our most base class which just declares the members:
template <class T>
class members {
public:
members(T* f) : m_foo(f) { }
protected:
T* const m_foo;
size_t m_offset = 0;
};
and then we write some wrappers around it to add reading:
template <class T>
struct reader : T {
using T::T;
Foo readAndAdvance() {
return this->m_foo[this->m_offset++];
};
};
and writing:
template <class T>
struct writer : T {
using T::T;
void writeAndAdvance(Foo const& f) {
this->m_foo[this->m_offset++] = f;
}
};
and then you just use those as appropriate:
using FooReader = reader<members<Foo const>>;
using FooWriter = writer<members<Foo>>;
using FooReaderWriter = writer<reader<members<Foo>>>;
CRTP.
template<class Storage>
class FooReaderImpl {
public:
FooData readFooDataAndAdvance() {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
return get_storage()->m_foo[get_storage()->m_offset++];
}
private:
Storage const* get_storage() const { return static_cast<Storage const*>(this); }
Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class Storage>
class FooWriterImpl {
public:
void writeFooDataAndAdvance(const FooData& foodata) {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
get_storage()->m_foo[get_storage()->m_offset++] = foodata;
}
private:
Storage const* get_storage() const { return static_cast<Storage const*>(this); }
Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class T>
struct storage_with_offset {
T* m_foo = nullptr;
std::size_t m_offset = 0;
};
struct FooReader:
FooReaderImpl<FooReader>,
storage_with_offset<const Foo>
{
FooReader(Foo const* p):
storage_with_offset<const Foo>{p}
{}
};
struct FooWriter:
FooWriterImpl<FooWriter>,
storage_with_offset<Foo>
{
FooWriter(Foo* p):
storage_with_offset<Foo>{p}
{}
};
struct FooReaderWriter:
FooWriterImpl<FooReaderWriter>,
FooReaderImpl<FooReaderWriter>,
storage_with_offset<Foo>
{
FooReaderWriter(Foo const* p):
storage_with_offset<Foo>{p}
{}
};
If you need an abstract interface for runtime polymorphism, inherit FooReaderImpl and FooWriterImpl from them.
Now, FooReaderWriter obeys the ducktype contract of FooReader and FooWriter. So if you use type erasure instead of inheritance, it will qualify for either (at point of use).
I'd be tempted to change them to
using FooReader = std::function<FooData()>;
using FooWriter = std::function<void(FooData const&)>;
and then implement a multi-signature std::function for FooReaderWriter. But I'm strange and a bit unhinged that way.
How can I do memory allocation like this:
template <class T>
class A
{
T Generate()
{
return new T(); // Error
}
};
A<B *> a;
B * pI = A.Generate();
Or can you only ever define something as:
A<B> a;
If I understood you correctly you wish to have specialization of A for pointer types. Try something like:
#include <cstdio>
template <class T>
class A
{
public:
T *Generate()
{
printf("A\n");
return new T; // Error
}
};
template <class T>
class A<T*> {
public:
T *Generate() {
printf("Specialization of A\n");
return new T;
}
};
class B {
};
int main() {
A<B *> a;
B * pI = a.Generate();
}
Edit:
To "override" only a part of the functionality:
template <class T>
class Generator
{
public:
T *Generate()
{
printf("Generator\n");
return new T; // Error
}
};
template <class T>
class Generator<T*> {
public:
T *Generate() {
printf("Specialization of Generator\n");
return new T;
}
};
template <class T>
class A: public Generator<T>
{
public:
// does some other stuff
};
You have a bunch of issues with your code:
A.Generate() should be a.Generate() as Generate is a member function.
new T() returns T*, so your Generate function should look like:
T* Generate()
{
return new T();
}
This should also be reflected in your usage:
A<B *> a;
B ** pI = a.Generate(); //generator for B* returns B**
new will return a pointer so you need the signature
T* Generate()
{
return new T();
}
This function must also be public if you'd like to call it outside the class.
How can I do memory allocation like this
By fixing the syntax and semantic errors.
Return type of Generate is T, yet you return T*. You probably want to change the return type to T*. Accordingly, you must assign the returned type to a T* variable. Since your T is B*, that variable must have the type B**.
You try to call a non-static member of A without an instance. Call the member using the instance instead. Also, that member is private. Make it public.
When you've fixed the code, you'll see that it does memory allocation successfully.
Or can you only ever define something as:
A<B> a;
No, there are many more ways to define something.
I am not sure what to call it, but is something like this possible as the commented out line reflects?
template <typename T>
class Test
{
public:
Test(T& t) : m_t(t) {}
T* operator->() { return &m_t; }
private:
T& m_t;
};
class A
{
public:
static const int integer = 0;
void function() {}
};
int main()
{
A a;
Test<A> test(a);
test->function();
// Something similar to doing Test<A>::integer?
return 0;
}
Well, why don't you do:
test->integer;
You can always access static members the same way as non-static ones (i.e. from an instance variable).
The other option would be to define in Test:
template <typename T>
class Test
{
public:
typedef T value_type;
// ...
};
In which case you will be able to do:
Test<A>::value_type::integer;
which will avoid the need of creating an instance of Test<A>.
At last, if you are using C++11 and Test follows the smart pointers conventions, then you will have:
std::pointer_traits<Test<A> >::element_type::integer;
which has the advantage to work even if you replace Test<A> with A*.
No. In C++, "overloading" only makes sense for functions. Instead of mixing static and non-static items in a class, you could try making two separate classes, both with all non-static members. Return your value from a function call, rather than using a public static member variable.