CRTP and template template parameters limitation - c++

I'm trying to experiment with CRTP but I am puzzled on why the following code does not compile.
template<template<class...> class CBase>
struct ComponentX : public CBase<ComponentX>
{
// This does NOT compile
};
template<template<class...> class CBase>
struct ComponentY : public CBase<int>
{
// This does compile
};
Do you know if there is some limitation for template template parameters in the case of CRTP?

A class template name stands for the "current specialization" (i.e. it is an injected class name) only after the opening { of the class template definition, inside its scope. Before that, it's a template name.
So CBase<ComponentX> is an attempt to pass a template as an argument to CBase, which expects a pack of types.
The fix is fairly simple:
template<template<class...> class CBase>
struct ComponentX : public CBase<ComponentX<CBase>> // Specify the arguments
{
// This should compile now
};
ComponentX<CBase> is the name of the specialization you wish to provide as a type argument.

Related

How to enforce contract on template parameter

How can I specify template parameter to be of a certain type i-e it must have implemented an interface (the template parameter must be a derived class of a specific base class)
Heres the interface (abstract base class)
class baseActionCounter{
public:
virtual int eat()=0;
virtual int drink()=0;
};
Now I want my template parameter to be of type baseActionCounter
Heres the templated class
//imaginary template syntax in the line below. Is there a way of achieving this behavior?
template <class counterType : baseActionCounter>
class bigBoss{
counterType counter;
public:
int consumerStats(){
//I am able to call member function because I know that counter object has eat() and drink()
//because it implemented baseActionCounter abstract class
return counter.eat() + counter.drink();
}
};
I can also just derive my bigBoss class from baseActionCounter but I want to know how to achieve this behavior with templates.
Also, template specialization is not suitable as there is just one BigBoss class for any implementor of baseActionCounter class.
Yes, you can use std::is_base_of to check the type, e.g.
template <class counterType, std::enable_if_t<std::is_base_of_v<baseActionCounter, counterType>>* = nullptr>
class bigBoss {
Or
template <class counterType>
class bigBoss {
static_assert(std::is_base_of_v<baseActionCounter, counterType>, "counterType must derive from baseActionCounter");
...
};
Or use concept (since C++20).
template <class T>
concept Derived = std::is_base_of_v<baseActionCounter, T>;
template <Derived counterType>
class bigBoss {
BTW: std::is_base_of also returns true if the base class baseActionCounter is specified; if that's not what you want you can combine the condition with std::is_same.

Curiously recurring templates with template leaf classes

I am thinking about using curiously recurring template pattern for my application. However, I would like the classes to operate on the user defined types. I would like to understand if it is possible to create a structure similar to the one shown below:
template <class T_leaftype>
class BaseTrajectoryPoint {
};
template <class MyType>
class MyTrajectoryPoint: public BaseTrajectoryPoint<MyTrajectoryPoint> {
private:
MyType A;
};
The code above fails to compile with the following error:
type/value mismatch at argument 1 in template parameter list for ‘template class BaseTrajectoryPoint’
Are there any alternative ways of approaching the problem? I would like to use static polymorphism, but I would prefer to define all possible methods in the base class.
template <class T_leaftype>
class BaseTrajectoryPoint {
};
template <class MyType>
class MyTrajectoryPoint: public BaseTrajectoryPoint<MyTrajectoryPoint<MyType> > {
private:
MyType A;
};
MyTrajectoryPoint isn't a type, it's template; when you pass it as template parameter, it's seen as template<typename> class T>, not template<class T> - and the latter is what your base class is expecting. But MyTrajectoryPoint<MyType> names a type, so you can use it as template parameter of your base class.
Of course, you can change declaration of BaseTrajectoryPoint to template<template<class> class T_leaftype>, but then you would have to use class template as template parameter, never a complete type.
What our friend Griwes said is correct, although if you know that every class that will inherit BaseTrajectoryPoint is a template class, you can do the following:
template<template < class > class TLeaf> // << This means: It is expected a template class as parameter
class BaseTrajectoryPoint{
};
template <class MyType>
class MyTrajectoryPoint: public BaseTrajectoryPoint<MyTrajectoryPoint> >{
private:
MyType A;
};

Variable of a template class with a template class template parameter set to a base template of the derived template with the variable

I'm attempting to have a derived class (normal template) that has a variable of a template type that has as its template class parameter the type of a base class (normal template, same parameter as the derived class) of the derived class (the one with the variable). This makes VC++ incredibly angry at me, and I am incapable of calming its fury. Here's a quick example:
template<template<typename VT> class CT, typename VT> struct encapThing {};
template<typename VT> struct innocuousBase {};
template<typename VT> struct derivOfDoom : public innocuousBase<VT>
{
encapThing<innocuousBase, VT> ohgodhelp; //C3200
};
It will throw a C3200, saying it expected a class template. Now, I can see why this might be thinking there is a recursive loop of templates within templates, even if this isn't actually the case. How can I convince VC++ otherwise?
Unqualified use of innocuousBase inside of derivOfDoom<> is interpreted as innocuousBase<VT>, much as unqualified use of derivOfDoom in that context would be interpreted as derivOfDoom<VT>. I don't remember offhand whether or not this is standard-conformant behavior, but the workaround is trivial: fully qualify innocuousBase so the compiler knows you're referring to the innocuousBase class template and not the innocuousBase<VT> base class:
template<typename VT> struct derivOfDoom : innocuousBase<VT>
{
encapThing<::innocuousBase, VT> ohgodhelp;
};

Use template template class argument as parameter

Modern C++ Design gives the following example:
template <class T> struct EnsureNotNull
{
static void Check(T*& ptr)
{
if (!ptr) ptr = GetDefaultValue();
}
};
template
<
class T,
template <class> class CheckingPolicy = EnsureNotNull,
template <class> class ThreadingModel
>
class SmartPtr
: public CheckingPolicy<T>
, public ThreadingModel<SmartPtr>
{
...
T* operator->()
{
typename ThreadingModel<SmartPtr>::Lock guard(*this);
CheckingPolicy<T>::Check(pointee_);
return pointee_;
}
private:
T* pointee_;
};
I couldn't figure how ThreadingModel template would be constructed in a fashion that It could accept SmartPtr as parameter, in my mind some crazy recursion is going to happen. How can this be possible?
Edit:
I've tried Potatoswatter (sorry lol) comment:
template <class SmartPtr> struct SingleThreadingModel
{
class Lock
{
public:
Lock(SmartPtr&)
{
}
};
};
but it did'nt worked.
here is the error that gcc is giving me:
main.cpp:28:35: error: type/value mismatch at argument 1 in template parameter list for ‘template<class> class ThreadingModel’
main.cpp:28:35: error: expected a type, got ‘SmartPtr’
You are trying to pass SmartPtr as a template type argument to ThreadingModel. SmartPtr however is a template, not a concrete type, and the injected class-name is not available in the inheritance list.
Also note that you can't just use default arguments for template parameters in arbitrary positions (§14.1/11):
If a template-parameter has a default template-argument, all subsequent template-parameters shall have a default template-argument supplied.
Your code with those issues fixed:
template
<
class T,
template <class> class ThreadingModel,
template <class> class CheckingPolicy = EnsureNotNull
>
class SmartPtr
: public CheckingPolicy<T>
, public ThreadingModel<SmartPtr<T, ThreadingModel, CheckingPolicy> >
// ^ .... now passing a concrete class .... ^
{
T* operator->() {
// the following use of SmartPtr is fine as it is the injected class-name:
typename ThreadingModel<SmartPtr>::Lock guard(*this);
// ...
}
};
Note that while Modern C++ Design is an excellent book, it can't replace a good basic book on templates like Vandevoorde/Josuttis.
The recursion is OK because passing a specialization as a template parameter does not directly cause it to be instantiated.
(ThreadingModel<SmartPtr> in the base list is just shorthand for ThreadingModel< SmartPtr< T, CheckingPolicy, ThreadingModel > > which uses the "current specialization.")
I don't know what ThreadingModel is supposed to do, so I can't implement it, but it should have a declaration of the form
template< class Client > class MyThreading
and it cannot access anything inside Client outside of MyThreading member functions. If you use Client and Client depends on MyThreading, then infinite recursion does happen.

How to forward declare the following template class

I try to forward declare concurrent_bounded_queue ;
class MyClass {
namespace tbb {
template<typename T> class cache_aligned_allocator;
template<class T, class A = cache_aligned_allocator> class concurrent_bounded_queue;
};
// I wish to maintain this syntax.
tbb::concurrent_bounded_queue<std::string>& concurrentBoundedQueue;
}
I get the following error :
error C3203: 'cache_aligned_allocator' : unspecialized class template can't be used as a template argument for template parameter 'A', expected a real type
error C2955: 'tbb::cache_aligned_allocator' : use of class template requires template argument list c:\projects\vitroxreport\src\Executor.h(21) : see declaration of 'tbb::cache_aligned_allocator'
May I know how I can avoid?
Thanks.
Allocator is a template, but second argument of the queue is concrete class. Try this:
class MyClass {
namespace tbb {
template<typename T> class cache_aligned_allocator;
template<class T, class A = cache_aligned_allocator<T> >
class concurrent_bounded_queue;
};
tbb::concurrent_bounded_queue<std::string>& concurrentBoundedQueue;
};