Say I have a template class:
template <typename T> class StringDeque:
public std::deque<T>
{
public:
...
private:
typedef std::deque<T> BaseClass;
};
Say I want to create concrete class ArrayString where T=std::string.
What is the proper way to achieve that:
define
#define ArrayString StringDeque<string>
typedef
typedef StringDeque < string > ArrayString;
inheritance
class ArrayString :
public StringDeque < string > {};
I'm not sure whether all of the suggestions are valid. Anyway I'm trying to figure out which practice most fits.
You want to give a name to a particular type (which happens to be an instantiation of a template). The C++ way to give names (aliases) to types is with a typedef:
typedef StringDeque<string> ArrayString;
Or, since C++11, with an alias declaration:
using ArrayString = StringDeque<string>;
Side note: when thinking about this stuff, it generally helps to think in terms of the correct template terminology. You don't have a "template class" (a class with special properties), you have a "class template" (a template for creating classes). The template is not a type; its instantiations are.
Proper ways:
typedef std::deque<std::string> StringDeque;
using StringDeque = std::deque<string>;
That said, here are some more notes on your question:
You wrote:
template <typename T> class StringDeque: public std::deque<T> // ...
std container classes are not meant as base classes. This means they should not be inherited from (and inheriting from them is UB).
When you want to use std container functionality in another class, the answer should always be encapsulation. That means, the correct way to build on the functionality of std::queue in your classes is:
template<typename T> class StringDequeue {
std::deque<T> storage;
public:
// add interface here that uses storage
};
You also proposed this:
#define ArrayString StringDeque<string>
Please never use define to declare a type. It works, but it has it's own pitfalls and is considered bad practice in C++.
Create type aliases with typedef or (since C++11) using:
typedef StringDeque<std::string> ArrayString;
using ArrayString = StringDeque<std::string>;
The preprocessor is a sledgehammer: occasionally useful but, with no knowledge of language-level constructs, prone to break code in surprising ways. In this case, I think the only problem is that the name won't be constrained to any scope; but that's reason enough to avoid it.
Inheritance creates a new type; while it's usually usable where the base type is, it's not completely interchangable, and doesn't inherit any constructors from the base type. So that's also not what you want for a simple type alias.
Related
I am trying to achieve something like the following:
template <typename T>
class MyClass {
struct nested {
using OtherT = // Some type derived from T
};
protected:
// Any way to avoid `typename` here?
typename nested::OtherT member;
};
Is there any way to use types nested in a struct / namespace / other, that are computed from the template type T, without using needing the typename keyword?
I am open to declaring nested in any other way, its purpose is only to hold types.
Edit: the reason I am trying to do this is that I will have a whole collection of types derived from T already visible inside of MyClass, and then closely related variants of those same types inside of nested. I am hoping to use nested to tell them apart in a clear and concise way.
This situation will appear in many places over the codebase, so I was hoping to avoid to see typename everywhere these types are mentioned.
Don't put them in a type like that. If you need to tell them apart, give them a more descriptive name. Even if your preferred mechanism worked, you'd have to prefix the names with nested:: anyway. So just make that part of the name and get it over with:
template <typename T>
class MyClass {
using nested_OtherT = // Some type derived from T
protected:
nested_OtherT member;
};
Suppose we have a templated class,
template<typename Type>
class Container {};
Of course, we can't do this:
struct Foo
{
Container _container;
}
But what if we wanted to do something like it? Is the only way to do this, to define a base class,
class ContainerBase {};
template<typename Type>
class Container : public ContainerBase {};
and store a pointer, like below?
struct Foo
{
ContainerBase* _container;
}
It's simple enough, but it feels weird to have to add a base class solely for that reason, when it seems the compiler should have enough information to imply a set of related specializations. Of course, regardless _container needs to be a pointer, else Foo couldn't resolve to a static size, but
struct Foo
{
Container* _container;
}
doesn't work either.
it seems the compiler should have enough information to imply a set of related specializations.
Nope. Template specializations are totally unrelated except in name, and the name of a type has essentially no bearing on runtime operation. Specializations of a given template usually share a (mostly) common interface, but they could just as well be completely different.
Adding a base class is essential if you want to relate between the specializations. And if they share so much in common, factoring that functionality into the base is a pretty great idea.
Catch-all traits classes like std::iterator_traits are useful by separating the properties of a type from its definition, so for example the properties may be made available before the definition is complete.
Defining traits in addition to each client class itself is inconvenient, because the traits typically also have a place as members. This is why the generic implementation of std::iterator_traits is defined in terms of its template argument's members.
template< typename it >
struct iterator_traits {
typedef typename it::category category;
typedef typename it::value_type value_type;
// etc
};
Isn't it easier, and less work for the compiler, to use inheritance instead?
template< typename t >
struct t_traits : public t {
t_traits() = delete; // Prevent runtime instances.
};
This fails to document the interface in the primary template, but there are other opportunities for that anyway.
It seems pointless to write lots of repetitive code to define a meta-container class, in an idiom which doesn't even guarantee prevent such abuse as creation at runtime.
Or maybe that's entirely backwards. In addition to std::iterator_traits we also have std::iterator, a pseudo-abstract base class with mostly the same members. Such redundancy is a code smell. Wouldn't it be better if custom iterators looked like this?
template<>
struct iterator_traits< struct my_iterator > {
typedef random_access_iterator_tag category;
typedef foo value_type;
...
};
struct my_iterator : iterator_traits< struct my_iterator > {
...
};
(For the sake of argument, let's ignore the fact that an actual std::iterator_traits specialization must be declared in namespace std. I'm trying to make a familiar illustration of something that might happen in user code.)
This is cleaner in that the idiom need not be violated to handle whatever exceptional case necessitated the fancy footwork in the first place. Instead of the primary traits template producing an internal error that the missing client class is unsuitable for something, there need not be any primary traits template at all.
It's conceptually better to separate qualities of a class from implementation of its services, regardless of whether that separation is necessary. BUT, this style does require breaking every client class into two pieces, including an explicit specialization, which is sort of ugly.
Is anyone familiar with this design space? I'm leaning toward the second idiom, although it looks unusual in practice. But there are probably ins and outs known to those who have trod here before.
The problem with user-defined traits as a specialization of a library type is that a library type belongs to the library. Defining an explicit specialization requires opening the library namespace, which is ugly.
Alternatives 1 and 2 can be combined into a best of both worlds pattern that
always allows optimal separation of concerns (by splitting a class into traits and implementation)
doesn't require splitting a class
never requires opening the library namespace
An extra bit of glue is needed in the form of an ADL based metafunction mapping any class to its traits.
template< typename t >
t traits_type_entry( t const & ); // Declared, never defined.
template< typename t >
using traits_type = decltype( traits_type_entry( std::declval< t >() ) );
By default, T serves as its own traits type as traits_type< T >::type is T. To change this for a given type t to a traits class t_traits, declare (but do not define) a function t_traits traits_type_entry( t const & ). This t_traits class may or may not be a base class of t; the traits_type facility doesn't care. Because the function will be found by argument-depenedent lookup, it may be declared as a friend function with no declaration at namespace scope.
Usage nested inside a class (just to make a difficult test-case) looks like this. For usual usage in a namespace just drop the friend keyword.
class outer_scope {
struct special;
struct special_traits {
typedef int value_type;
constexpr static int limit = 5;
};
friend special_traits traits_type_entry( special const & );
struct unspecial {
typedef double baz_type;
int table[ util::traits_type< special >::limit ];
};
struct special : special_traits {
void f() {
std::pair< typename util::traits_type< unspecial >::baz_type,
value_type >();
}
};
};
http://ideone.com/QztQ6i
Note, the t const & parameter to traits_type_entry can be simply t as long as the class is copyable and destructible.
Also, you could prevent declaring an object of (non-customized) trait type by having the primary template return a type derived from t with its constructor deleted, instead of t itself.
I'm exploring template shenanigans in C++ (C++11), and one thing I'd like to have is a pure virtual type in an abstract class. This would be like Scala's abstract types. In C++ I'd want to do something like the following:
struct Base {
// Says any concrete subclass must define Type, but doesn't
// require that it be anything in particular.
virtual typedef MyType;
};
struct Derived : Base {
// Won't compile unless this typedef exists.
typedef int MyType;
};
Any idea how to do this?
I am not sure there is a real need for this in C++.
Trying to put myself in the position of a designer who is looking for such a solution, I would say this kind of constraint would be needed to enforce some types to adhere to some syntactic convention.
Most likely, this is needed because a generic algorithm requires that syntactic convention: it cannot work with types that do not define such a type association.
For instance, the algorithm below requires the type of its argument to have an associated bar_type:
template<typename T>
bool foo(T t)
{
typedef typename T::bar_type FT;
FT ft;
...
}
But if this is the case, there is no need for enforcing a typedef to effectively constraint the input of foo<>(): simply omitting a type definition for bar_type won't make it possible to use that type with foo<>().
Of course, you would discover this only as soon as you actually try to do so, and not before. And being able to define a concept such as HasBarType, and then to enforce some types to realize that concept would be nice; on the other hand, concepts are not yet part of C++ and, as much as they are desirable, it is possible to live without them.
edit
This doesn't work, but I think the curiously recurring template pattern might be the way to go.
/edit
template<typename Dependent>
class Base : public Dependent {
typedef typename Dependent::MyType MyType;
};
Then use the curiously recurring template pattern:
struct Derived : Base<Derived> {
// Won't compile unless this typedef exists.
typedef int MyType;
};
Initially I was trying to typedef a template class and I got to the "gotw 79" article.
And I didn't want to create another class so I ended up doing the following. Basically typedef'ing inside the same class. It works obviously. but is it a good practice?
template <typename T,typename L>
class MyClass{
typedef std::tr1::shared_ptr<MyClass<T,L> > shrdPtr;
}
Thank you.
Well, I'm not a big fan of it unless you are designing MyClass to be specifically used only within shared_ptr objects, at which point I would insist that requirement be enforced.
It's a little ridiculous to put typedefs for every unrelated template instantiation that you might use with a given object. Just because you might put MyClass in a shared_ptr is not a good reason to typedef it there. You going to put typedefs for std::vector, map, list, unordered_map, set, deque,....etc, etc, etc?
But if MyClass extends shared_from_this and has private/protected constructors so that it can ONLY be created and immediately assigned to a shared_ptr then...sure...it's part of the interface.
If you're trying to avoid having to type out long parameter lists to instantiate a shared_ptr for a templated type with lots of parameters then a better bet is an EXTERNAL utility object just like shown in the article you cited:
template < typename T >
struct instantiate_shared_ptr { typedef shared_ptr<T> type; };
template < typename after typename > struct my_complex_template {};
typedef my_complex_template<some parameters> mct_1;
typedef instantiate_shared_ptr<mct_1>::type mct_1_sp;
Yes, especially if the name MyClass_sp is referred to in client code.
This is probably good practice, it makes it simpler if you decide to change the underlying class the typedef refers to at a later date and (arguably) saves typos as well as making code easier to read even if it never changes. The particular choice of name here MyClass_sp leaves a little to be desired in my opinion though.
Also it's worth thinking carefully if making the typedef public or private is most appropriate, i.e. is it part of your public interface?
It's good, IMO.
I used it a lot.
If I want to use a container which element is the type of the my template, I typedef it.
Such as,
template <typename T>
class MyClass {
private:
typedef std::list<T> SomeContainerType;
typedef SomeContainerType::iterator IteratorType;
Then if I find any other structure more suitable, such as a vector, I can change the type without touching too much code.
A better solution for smart pointer typedefs is to do them after the class definition in the header:
namespace N
{
class A
{
};
typedef std::tr1::shared_ptr<A> APtr;
}
This keeps your smart pointer defintion near your class definition while preventing you (and any developers using your code) from having to write code like A::APtr a(new A) (which just looks odd).
EDIT: Since he is concerned with a template class:
namespace N
{
template<class T, class L>
class A
{
};
template<class T, class L>
struct A_Instantiator
{
typedef std::tr1::shared_ptr<A<T, L> > APtr;
};
}