In order to use a library, I need to use different classes that have the same base name. I.e.
MyClass
MyClassImpl
PreMyClass
And so on. in order to use them with template I need pass all these class names.
template <typename T, typename TImpl, typename PreT>
class ClassThatUsesAllTheseObjects
{
public:
ClassThatUsesAllTheseObjects();
private:
T myClass;
TImpl myClassImpl;
PreT myPreClass;
};
It's possibile to obtain the same result giving only the principal MyClass as template argument, building other names when needed?
Am not sure of the settings in your question, but in some cases, you might want to do something like the trait mechanism.
Suppose you write a concrete MyClass, and others like it. For each group of concrete classes, you do something like:
// This is common
template <typename T>
struct foo_traits
{
};
// This is for each set of concrete classes
template<>
struct foo_traits<MyClass>
{
using Impl = MyClassImpl;
using Pre = PreMyClass;
};
...
Then you use the traits class like this:
template <
typename T,
class Impl = typename foo_traits<T>::Impl,
class Pre = typename foo_traits<T>::Pre>
class ClassThatUsesAllTheseObjects
{
public:
ClassThatUsesAllTheseObjects();
private:
T myClass;
Impl myClassImpl;
Pre myPreClass;
};
This allows you to explain what are the "natural friends" of your principal concrete classes.
I can think of two options:
Use a tag to make the difference. So you would have e.g. a template <class T> myClass, a struct impl_t{}, struct pre_t{}, struct base_t{} and then use gthem in this way:
code:
myClass<base_t> ; // instead of plain MyClass
myClass<impl_t> ;// instead of plain MyClassImpl
myClass<pre_t> ; // instead of myPreClass
Template specialization should make their definition easy/possible.
traits, but there is already an answer on that side :-)
Related
I want to create a class that accepts only certain types of template classes. I know that there exists template specialization, but I want my class to accept all templates that implement a specific function, search.
Let's say I have a class A as follows:
template<class T> //add a restriction that T implements bool search(T)
class A
{
T t;
//do something that uses T.search(T x)
if(t.search(x))
//Do something
};
So basically, I want to create a generic class that works for all classes that have the search functionality. Is there a way to do this?
I want to create a generic class that works for all classes that have the search functionality. Is there a way to do this?
By example, using decltype() as follows
template <typename T,
typename = decltype(std::declval<T>().search(std::declval<T>()))>
class A
{
};
The following is a full compiling example for a size() enabled class A
#include <string>
#include <type_traits>
template <typename T, typename = decltype(std::declval<T>().size())>
class A
{
};
int main()
{
A<std::string> as;
//A<int> ai; // compilation error
}
There is a drawback in this solution: you can hijack it explicating the second template parameter; by example, the following code compile
A<int, void> ai;
To avoid this problem you can use partial specialization as follows
template <typename T,
typename = decltype(std::declval<T>().search(std::declval<T>()))>
class A;
template <typename T>
class A<T>
{
};
Situation
I'm trying to implement a container that holds a specific data type - let's call it C. The container(let's call it B) is an inner class of A. I'm trying to declare the template but am running into compiler issues and am not sure what I should do.
Attempts
template <typename T<C>>
class A
{
class B
{
typedef std::unique_ptr<T> containerPtr;
private:
containerPtr container;
}
}
typedef std::shared_ptr<A<std::vector<C>>> somePtr; // Error!
The error is:
struct C
type name is not allowed
template <typename T,U>
class A
{
class B
{
typedef std::unique_ptr<T<U>> containerPtr;
private:
containerPtr container; // But does it contain C or some other type now?
// We have to do a check - what's the best approach?
}
}
typedef std::shared_ptr<A<std::vector<C>>> somePtr;
What is the best approach in this situation?
If you know for a fact that T is a template container storing some type C, then you don't need to specify C anywhere and can just templatize over T:
template <typename T>
class A {
class B {
/* Use the type name T however you'd like. */
};
};
This works because T has to be the name of a complete type, so if you do something like
A<std::vector<int>>
then T is std::vector<int> and any time you use T it will specifically be a std::vector of ints and not of any other type.
On the other hand, if you want the client to provide the name of a template class and then forcibly instantiate it with your choice of C, you can use template template arguments, like this:
template <template <typename...> class T>
class A {
class B {
typedef std::unique_ptr<T<C>> containerPtr;
/* ... use containerPtr ... */
};
};
This asks the user to give you a template type, so you'd write something like
A<std::vector> myObject;
and your A template will then instantiate std::vector using the type C.
EDIT: I didn't actually get a chance to test out any of the suggested solutions as I went on a vacation, and by the time I was back, the people responsible for the class template had made some changes that allowed me to get around the need to use types defined in the class template itself.
Thanks to everyone for their help though.
In a nutshell - and feel free to correct my wording, templates are still a bit of voodoo to me, - I need to know if I can use a (protected) struct or a #typedef defined inside a class template from my specialized class. For example:
This is the class template:
template<typename T>
class A : public C<T>
{
protected:
struct a_struct { /* Class template implementation, doesn't depend on T */ };
void foo( a_struct a );
};
Which I need to fully specialize for T = VAL:
template<>
class A< VAL > : public C< VAL >
{
void foo( a_struct a )
{
// My implementation of foo, different from the class template's
}
};
If I do something like this, however, the compiler complains that a_struct is undefined in my specialized class. I tried specializing and inheriting from the class template but that got... messy.
I saw some solutions, but all of them involved modifying the class template, which is something I am not able to easily do (different team).
Thoughts?
No, you can't use members of the primary template declaration in your specialization of the class template. That is because in essence a template class specialization declares a completely new class template that is applied when the template arguments match the specialization.
You have two options available though, if you want to do something like in your example:
You can specialize the template class member function. This is useful if it is indeed only one member function that is special (or at least the number of member functions is limited).
You can bring the declaration of the member (-type) in a common base class.
Since you indicated in an edit that you can't change the class template itself, specializing the member function seems the best option.
A simplified example of specializing a member function only
template< class T>
class Printer
{
public:
struct Guard {};
void DoPrint( const T& val)
{
Guard g;
(void)g;
std::cout << val << '\n';
}
};
struct Duck {};
template<>
void Printer<Duck>::DoPrint( const Duck& val)
{
Guard g;
(void)g;
std::cout << "Some duck\n";
}
The Guard here is only used to demonstrate that this type is available to both the primary and the specialized implementation of DoPrint().
It's not beautiful, but you can do it like this:
template<typename T>
class C
{
};
template<typename T>
class A : public C<T>
{
protected:
friend A<int>;
// ^^^^^^
struct a_struct { /* Class template implementation, doesn't depend on T */ };
void foo( a_struct a );
};
template<>
class A< int > : public C< int >
{
using a_struct = typename A<void>::a_struct;
// ^^^^^^
void foo( a_struct a )
{
// My implementation of foo, different from the class template's
}
};
or how about, re-declaring struct a_struct in the specialized template, with same functionality as default one.
I know it may not sound good since you need to inject in all specialized templates. But that is one i can think of now.
I've been designing classes that all follow the idiom that their shared pointer type is available in their namespace using code like this:
class ClassName
{
public:
typedef std::shared_ptr<ClassName> ptr;
}
making it possible to write code like this:
ClassName::ptr p=std::make_shared<ClassName>();
Now, I'd like to factor that code into a base class. It'll let me magically add weak_ptr support in just one place, and will indicate which classes conform to this protocol, and generally make it easier to build out the capabilities.
Am I forced into using templates?
One way is to follow the enable_shared_from_this<> approach.
template <typename T>
class SmartPointer
{
public:
typedef std::shared_ptr<T> ptr;
typedef std::weak_ptr<T> wptr;
}
allowing me to define classes like this:
class ClassName:public SmartPointer<ClassName>
...
but it strikes me as clumsy to have to specify ClassName as the template parameter. What I'd like to do is be able to identify the type of the derived class at compile time, substituting in for derivedClassType() below:
class SmartPointer
{
public:
typedef std:shared_ptr<derivedClassType()> ptr;
typedef std:weak_ptr<derivedClassType()> wptr;
}
class ClassName: public SmartPointer
...
it seems like the compiler must know that information at compile time as a sort of implied template parameter... This has the added benefit of ensuring the type referenced is the derived class and not a base class.
Is there a syntax that would make this possible?
The approach used by enable_shared_from_this is called CRTP, the "curiously recurring template pattern." You need to specify the derived class; there is no way for a base class template to ask who is using it as a base class.
But you could inherit from enable_shared_from_this in your CRTP base and avoid specifying the derived class name twice.
So, I'm going to cheat by changing your syntax.
template<typename T>
using Ptr = T::template ptr<T>;
template<typename T>
using WPtr = T::template wptr<T>;
struct Pointerable {
template<typename T>
using ptr = std::shared_ptr<T>;
template<typename T>
using wptr = std::weak_ptr<T>;
};
class Foo : public Pointerable {
};
Ptr<Foo> x = std::make_shared<Foo>( ... );
WPtr<Foo> y = x;
here, the only purpose of Pointerable is to tag your types that you want to be used. Ptr<Foo> and WPtr<Foo> could be written to work on any class:
template<typename T>
using Ptr = std::shared_ptr<T>;
template<typename T>
using WPtr = std::weak_ptr<T>;
but I suppose you want to be able to tag objects as "managed by smart pointers" for whatever reason.
We could even make Pointerable completely empty, if you don't like that template boilerplate in it:
struct Pointerable {};
and add a traits class:
template<typename T>
struct is_pointerable : std::is_base_of<Pointerable, T> {};
which we then use:
template<typename T, typename=typename std::enable_if< is_pointerable<T>::value >::type >
using Ptr = std::shared_ptr<T>;
but I think the boilerplate works better -- SFINAE using seems overkill.
I have my templated container class that looks like this:
template<
class KeyType,
class ValueType,
class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>,
class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType>
>
class MyClass
{
[...]
}
Which means that when I instantiate an object of this class, I can do it several different ways:
MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;
Those are all good. The problem comes when I want to instantiate a MyClass that uses a non-default version of the ValueCompareFunctor argument, but I still want to use the default value of the KeyCompareFunctor argument. Then I have to write this:
MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;
It would be much more convenient if I could somehow omit the third argument and just write this:
MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;
Since the MyCustomValueCompareFunctor works only on objects of type MyValueType and not on objects of type MyKeyType, it seems like the compiler could at least theoretically work out what I meant here.
Is there a way to do this in C++?
In general, both in templates and functions or methods, C++ lets you use default for (and thereby omit) only trailing parameters -- no way out.
I recommend a template or macro to shorten AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> to Foo<MyKeyType> -- not perfect, but better than nothing.
No. The closest you can come is to allow users to specify some sentinel type - like void - meaning "use default value here", and use template metamagic inside your class to typedef the real default if void was given to you. But this probably isn't a good idea from readability point of view.
Boost parameters and Boost graph named parameters are efforts towards naming parameters for template functions/methods. They give the opportunity to provide arguments in whichever order you prefer. Some arguments may be optional, with default values.
The same approach may be applied to template arguments. Instead of having N template arguments + P optional ones, create your class with N+1 template arguments. The last one will hold "named" parameters which can be omitted.
This answer is not complete yet, but i hope it's a good start !
An alternative option is to use Traits classes:
template <class KeyType>
class KeyTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
};
template <class ValueType>
class ValueTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare;
};
template<class KeyType class ValueType>
class MyClass
{
typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
};
Then if you have a type which needs a different comparison function for Key's, then you'd explicitly specialize the KeyTraits type for that case. Here's an example where we change it for int:
template <>
class KeyTraits<int>
{
typedef SpecialCompareForInt Cmopare;
};
There is another option, which uses inheritance and which works like the following. For the last two arguments, it uses a class that inherits virtually from a class that has two member templates, that can be used to generate the needed types. Because the inheritance is virtual, the typedefs it declares are shared among the inheritance as seen below.
template<class KeyType,
class ValueType,
class Pol1 = DefaultArgument,
class Pol2 = DefaultArgument>
class MyClass {
typedef use_policies<Pol1, Pol2> policies;
typedef KeyType key_type;
typedef ValueType value_type;
typedef typename policies::
template apply_key_compare<KeyType>::type
key_compare;
typedef typename policies::
template apply_value_compare<ValueType>::type
value_compare;
};
Now, have a default argument that you use, which has typedefs for the default arguments you want provide. The member templates will be parameterized by the key and value types
struct VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType>
type;
};
template<typename ValueType>
struct apply_value_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>
type;
};
};
struct DefaultArgument : virtual VirtualRoot { };
template<typename T> struct KeyCompareIs : virtual VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef T type;
};
};
template<typename T> struct ValueCompareIs : virtual VirtualRoot {
template<typename ValueType>
struct apply_value_compare {
typedef T type;
};
};
Now, use_policies will derive from all the template arguments. Where a derived class of VirtualRoot hides a member from the base, that member of the derived class is dominant over the member of the base, and will be used, even though the base-class member can be reached by other path in the inheritance tree.
Note that you don't pay for the virtual inheritance, because you never create an object of type use_policies. You only use virtual inheritance to make use of the dominance rule.
template<typename B, int>
struct Inherit : B { };
template<class Pol1, class Pol2>
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
{ };
Because we potentially derive from the same class more than once, we use a class template Inherit: Inheriting the same class directly twice is forbidden. But inheriting it indirectly is allowed. You can now use this all like the following:
MyClass<int, float> m;
MyClass<float, double, ValueCompareIs< less<double> > > m;