I understand the mechanics of static polymorphism using the Curiously Recurring Template Pattern. I just do not understand what is it good for.
The declared motivation is:
We sacrifice some flexibility of dynamic polymorphism for speed.
But why bother with something so complicated like:
template <class Derived>
class Base
{
public:
void interface()
{
// ...
static_cast<Derived*>(this)->implementation();
// ...
}
};
class Derived : Base<Derived>
{
private:
void implementation();
};
When you can just do:
class Base
{
public:
void interface();
}
class Derived : public Base
{
public:
void interface();
}
My best guess is that there is no semantic difference in the code and that it is just a matter of good C++ style.
Herb Sutter wrote in Exceptional C++ style: Chapter 18 that:
Prefer to make virtual functions private.
Accompanied of course with a thorough explanation why this is good style.
In the context of this guideline the first example is good, because:
The void implementation() function in the example can pretend to be virtual, since it is here to perform customization of the class. It therefore should be private.
And the second example is bad, since:
We should not meddle with the public interface to perform customization.
My question is:
What am I missing about static polymorphism? Is it all about good C++ style?
When should it be used? What are some guidelines?
What am I missing about static polymorphism? Is it all about good C++ style?
Static polymorphism and runtime polymorphism are different things and accomplish different goals. They are both technically polymorphism, in that they decide which piece of code to execute based on the type of something. Runtime polymorphism defers binding the type of something (and thus the code that runs) until runtime, while static polymorphism is completely resolved at compile time.
This results in pros and cons for each. For instance, static polymorphism can check assumptions at compile time, or select among options which would not compile otherwise. It also provides tons of information to the compiler and optimizer, which can inline knowing fully the target of calls and other information. But static polymorphism requires that implementations be available for the compiler to inspect in each translation unit, can result in binary code size bloat (templates are fancy pants copy paste), and don't allow these determinations to occur at runtime.
For instance, consider something like std::advance:
template<typename Iterator>
void advance(Iterator& it, ptrdiff_t offset)
{
// If it is a random access iterator:
// it += offset;
// If it is a bidirectional iterator:
// for (; offset < 0; ++offset) --it;
// for (; offset > 0; --offset) ++it;
// Otherwise:
// for (; offset > 0; --offset) ++it;
}
There's no way to get this to compile using runtime polymorphism. You have to make the decision at compile time. (Typically you would do this with tag dispatch e.g.)
template<typename Iterator>
void advance_impl(Iterator& it, ptrdiff_t offset, random_access_iterator_tag)
{
// Won't compile for bidirectional iterators!
it += offset;
}
template<typename Iterator>
void advance_impl(Iterator& it, ptrdiff_t offset, bidirectional_iterator_tag)
{
// Works for random access, but slow
for (; offset < 0; ++offset) --it; // Won't compile for forward iterators
for (; offset > 0; --offset) ++it;
}
template<typename Iterator>
void advance_impl(Iterator& it, ptrdiff_t offset, forward_iterator_tag)
{
// Doesn't allow negative indices! But works for forward iterators...
for (; offset > 0; --offset) ++it;
}
template<typename Iterator>
void advance(Iterator& it, ptrdiff_t offset)
{
// Use overloading to select the right one!
advance_impl(it, offset, typename iterator_traits<Iterator>::iterator_category());
}
Similarly, there are cases where you really don't know the type at compile time. Consider:
void DoAndLog(std::ostream& out, int parameter)
{
out << "Logging!";
}
Here, DoAndLog doesn't know anything about the actual ostream implementation it gets -- and it may be impossible to statically determine what type will be passed in. Sure, this can be turned into a template:
template<typename StreamT>
void DoAndLog(StreamT& out, int parameter)
{
out << "Logging!";
}
But this forces DoAndLog to be implemented in a header file, which may be impractical. It also requires that all possible implementations of StreamT are visible at compile time, which may not be true -- runtime polymorphism can work (although this is not recommended) across DLL or SO boundaries.
When should it be used? What are some guidelines?
This is like someone coming to you and saying "when I'm writing a sentence, should I use compound sentences or simple sentences"? Or perhaps a painter saying "should I always use red paint or blue paint?" There is no right answer, and there is no set of rules that can be blindly followed here. You have to look at the pros and cons of each approach, and decide which best maps to your particular problem domain.
As for the CRTP, most use cases for that are to allow the base class to provide something in terms of the derived class; e.g. Boost's iterator_facade. The base class needs to have things like DerivedClass operator++() { /* Increment and return *this */ } inside -- specified in terms of derived in the member function signatures.
It can be used for polymorphic purposes, but I haven't seen too many of those.
The link you provide mentions boost iterators as an example of static polymorphism. STL iterators also exhibit this pattern. Lets take a look at an example and consider why the authors of those types decided this pattern was appropriate:
#include <vector>
#include <iostream>
using namespace std;
void print_ints( vector<int> const& some_ints )
{
for( vector<int>::const_iterator i = some_ints.begin(), end = some_ints.end(); i != end; ++i )
{
cout << *i;
}
}
Now, how would we implement int vector<int>::const_iterator::operator*() const; Can we use polymprhism for this? Well, no. What would the signature of our virtual function be? void const* operator*() const? That's useless! The type has been erased (degraded from int to void*). Instead, the curiously recurring template pattern steps in to help us generate the iterator type. Here is a rough approximation of the iterator class we would need to implement the above:
template<typename T>
class const_iterator_base
{
public:
const_iterator_base():{}
T::contained_type const& operator*() const { return Ptr(); }
T::contained_type const& operator->() const { return Ptr(); }
// increment, decrement, etc, can be implemented and forwarded to T
// ....
private:
T::contained_type const* Ptr() const { return static_cast<T>(this)->Ptr(); }
};
Traditional dynamic polymorphism could not provide the above implementation!
A related and important term is parametric polymorphism. This allows you to implement similar APIs in, say, python that you can using the curiously recurring template pattern in C++. Hope this is helpful!
I think it's worth taking a stab at the source of all this complexity, and why languages like Java and C# mostly try to avoid it: type erasure! In c++ there is no useful all containing Object type with useful information. Instead we have void* and once you have void* you truely have nothing! If you have an interface that decays to void* the only way to recover is by making dangerous assumptions or keeping extra type information around.
While there may be cases where static polymorphism is useful (the other answers have listed a few), I would generally see it as a bad thing. Why? Because you cannot actually use a pointer to the base class anymore, you always have to provide a template argument providing the exact derived type. And in that case, you could just as well use the derived type directly. And, to put it bluntly, static polymorphism is not what object orientation is about.
The runtime difference between static and dynamic polymorphism is exactly two pointer dereferenciations (iff the compiler really inlines the dispatch method in the base class, if it doesn't for some reason, static polymorphism is slower). That's not really expensive, especially since the second lookup should virtually always hit the cache. All in all, those lookups are usually cheaper than the function call itself, and are certainly worth it to get the real flexibility provided by dynamic polymorphism.
Related
I have a limited set of very different types, from which I want to store instances in a single collection, specifically a map. To this end, I use the type erasure idiom, ie. I have a non-templated base class from which the templated, type specific class inherits:
struct concept
{
virtual std::unique_ptr<concept> copy() = 0; // example member function
};
template <typename T>
struct model : concept
{
T value;
std::unique_ptr<concept> copy() override { ... }
}
I then store unique_ptrs to concept in my map. To retrieve the value, I have a templated function which does a dynamic cast to the specified type.
template <typename T>
void get(concept& c, T& out) {
auto model = dynamic_cast<model<T>>(&c);
if (model == nullptr) throw "error, wrong type";
out = model->value;
}
What I don't like about this solution is, that specifying a wrong T is only detected at runtime. I'd really really like this to be done at compile time.
My options are as I see the following, but I don't think they can help here:
Using ad hoc polymorphism by specifying free functions with each type as an overload, or a template function, but I do not know where to store the result.
Using CRTP won't work, because then the base class would need to be templated.
Conceptually I would need a virtual function which takes an instance of a class where the result will be stored. However since my types are fundamentally different, this class would need to be templated, which does not work with virtual.
Anyways, I'm not even sure if this is logically possible, but I would be very glad if there was a way to do this.
For a limited set of types, your best option is variant. You can operate on a variant most easily by specifying what action you would take for every single variant, and then it can operate on a variant correctly. Something along these lines:
std::unordered_map<std::string, std::variant<Foo, Bar>> m;
m["a_foo"] = Foo{};
m["a_bar"] = Bar{};
for (auto& e : m) {
std::visit(overloaded([] (Foo&) { std::cerr << "a foo\n"; }
[] (Bar&) { std::cerr << "a bar\n"; },
e.second);
}
std::variant is c++17 but is often available in the experimental namespace beforehand, you can also use the version from boost. See here for the definition of overloaded: http://en.cppreference.com/w/cpp/utility/variant/visit (just a small utility the standard library unfortunately doesn't provide).
Of course, if you are expecting that a certain key maps to a particular type, and want to throw an error if it doesn't, well, there is no way to handle that at compile time still. But this does let you write visitors that do the thing you want for each type in the variant, similar to a virtual in a sense but without needing to actually have a common interface or base class.
You cannot do compile-time type checking for an erased type. That goes against the whole point of type erasure in the first place.
However, you can get an equivalent level of safety by providing an invariant guarantee that the erased type will match the expected type.
Obviously, wether that's feasible or not depends on your design at a higher level.
Here's an example:
class concept {
public:
virtual ~concept() {}
};
template<typename T>
struct model : public concept {
T value;
};
class Holder {
public:
template<typename T>
void addModel() {
map.emplace(std::type_index(typeid(T)), std::make_unique<model<T><());
}
template<typename T>
T getValue() {
auto found = types.find(std::type_index(typeid(T)));
if(found == types.end()) {
throw std::runtime_error("type not found");
}
// no need to dynamic cast here. The invariant is covering us.
return static_cast<model<T>*>(found->second.get())->value;
}
private:
// invariant: map[type] is always a model<type>
std::map<std::type_index, std::unique_ptr<concept>> types;
};
The strong encapsulation here provides a level of safety almost equivalent to a compile-time check, since map insertions are aggressively protected to maintain the invariant.
Again, this might not work with your design, but it's a way of handling that situation.
Your runtime check occurs at the point where you exit type erasure.
If you want to compile time check the operation, move it within the type erased boundaries, or export enough information to type erase later.
So enumerate the types, like std variant. Or enumerate the algorithms, like you did copy. You can even mix it, like a variant of various type erased sub-algorithms for the various kinds of type stored.
This does not support any algorithm on any type polymorphism; one of the two must be enumerated for things to resolve at compile time and not have a runtime check.
I'm porting some Fortran90 code to C++ (because I'm stupid, to save the "Why?!").
Fortran allows specification of ranges on arrays, in particular, starting at negative values, eg
double precision :: NameOfArray(FirstSize, -3:3)
I can write this in C++ as something like
std::array<std::array<double, 7>, FirstSize> NameOfArray;
but now I have to index like NameOfArray[0:FirstSize-1][0:6]. If I want to index using the Fortran style index, I can write perhaps
template <typename T, size_t N, int start>
class customArray
{
public:
T& operator[](const int idx) { return data_[idx+start]; }
private:
std::array<T,N> data_;
}
and then
customArray<double, 7, -3> NameOfArray;
NameOfArray[-3] = 5.2;
NameOfArray[3] = 2.5;
NameOfArray[4] = 3.14; // This is out of bounds,
// despite being a std::array of 7 elements
So - the general idea is "Don't inherit from std::'container class here'".
My understanding is that this is because, for example, std::vector does not have a virtual destructor, and so should not (can not?) be used polymorphically.
Is there some other way I can use a std::array, std::vector, etc, and get their functions 'for free', whilst overriding specific functions?
template<typename T, size_t N>
T& std::array<T,N>::operator[](const int idx) { ... };
might allow me to override the operator, but it won't give me access to knowledge about a custom start point - making it completely pointless. Additionally, if I were to optimistically think all my customArray objects would have the same offset, I could hardcode that value - but then my std::array is broken (I think).
How can I get around this? (Ignoring the simple answer - don't - just write myArray[idx-3] as needed)
There's no problem with inheriting standard containers. This is only generally discouraged because this imposes several limitations and such an inheritance is not the way how inheritance was originally predicted in C++ to be used. If you are careful and aware of these limitations, you can safely use inheritance here.
You just need to remember that this is not subclassing and what this really means. In particular, you shouldn't use pointers or references to the object of this class. The problem might be if you pass a value of MyVector<x>* where vector<x>* was expected. You should also never create such objects as dynamic (using new), and therefore also delete these objects through the pointer to the base class - simply because destructor call will not forward to your class's destructor, as it's not virtual.
There's no possibility to prevent casting of the "derived pointer" to the "base pointer", but you can prevent taking a pointer from an object by overloading the & operator. You can also prevent creating objects of this class dynamically by declaring an in-class operator new in private section (or = delete should work as well).
Don't also think about private inheritance. This is merely like containing this thing as a field in private section, except the accessor name.
A range converter class could be the solution although you would need to make it yourself, but it would allow you to get the range size to initialize the vector and to do the conversion.
Untested code:
struct RangeConv // [start,end[
{
int start, end;
RangeConv(int s, int e) : start(s), end(e) { }
int size() const { return end - start; }
int operator()(int i) { return i - start; } // possibly check whether in range
}
RangeConv r(-3, 3);
std::vector<int> v(r.size());
v[r(-3)] = 5;
so should not (can not?) be used polymorphically.
Don't give up too soon. There are basically two issues to consider with inheritance in C++.
Lifetime
Such objects, derived classes with non-virtual destructors in the base, can be used safely in a polymorphic fashion, if you basically follow one simple rule: don't use delete anywhere. This naturally means that you cannot use new. You generally should be avoiding new and raw pointers in modern C++ anyway. shared_ptr will do the right thing, i.e. safely call the correct destructor, as long as you use make_shared:
std:: shared_ptr<Base> bp = std:: make_shared<Derived>( /* constructor args */ );
The type parameter to make_shared, in this case Derived, not only controls which type is created. It also controls which destructor is called. (Because the underlying shared-pointer object will store an appropriate deleter.)
It's tempting to use unique_ptr, but unfortunately (by default) it will lead to the wrong deleter being used (i.e. it will naively use delete directly on the base pointer). It's unfortunate that, alongside the default unique_ptr, there isn't a much-safer-but-less-efficient unique_ptr_with_nice_deleter built into the standard.
Polymorphism
Even if std::array did have a virtual destructor, this current design would still be very weird. Because operator[] is not virtual, then casting from customArray* to std:: array* would lead to the wrong operator[]. This isn't really a C++-specific issue, it's basically the issue that you shouldn't pretend that customArray isa std:: array.
Instead, just decide that customArray is a separate type. This means you couldn't pass an customArray* to a function expecting std::array* - but are you sure you even want that anyway?
Is there some other way I can use a std::array, std::vector, etc, and get their functions 'for free', whilst overloading specific functions?
This is a good question. You do not want your new type to satisfy isa std::array. You just want it to behave very similar to it. As if you magically copied-and-pasted all the code from std::array to create a new type. And then you want to adjust some things.
Use private inheritance, and using clauses to bring in the code you want:
template <typename T, size_t N, int start>
struct customArray : private std::array<T,N>
{
// first, some functions to 'copy-and-paste' as-is
using std::array<T,N> :: front;
using std::array<T,N> :: begin;
// finally, the functions you wish to modify
T& operator[](const int idx) { return data_[idx+start]; }
}
The private inheritance will block conversions from customArray * to std::array *, and that's what we want.
PS: I have very little experience with private inheritance like this. So many it's not the best solution - any feedback appreciated.
General thought
The recommendation not to inherit from standard vector, is because this kind of construct is often misunderstood, and some people are tempted to make all kind of objects inherit from a vector, just for minor convenience.
But this rule should'nt become a dogma. Especially if your goal is to make a vector class, and if you know what you're doing.
Danger 1: inconsistency
If you have a very important codebase working with vectors in the range 1..size instead of 0..size-1, you could opt for keeping it according to this logic, in order not to add thousands of -1 to indexes, +1 to index displayed, and +1 for sizes.
A valid approach could be to use something like:
template <class T>
class vectorone : public vector<T> {
public:
T& operator[] (typename vector<T>::size_type n) { return vector<T>::operator[] (n-1); }
const T& operator[] (typename vector<T>::size_type n) const { return vector<T>::operator[] (n-1); }
};
But you have to remain consitent accross all the vector interface :
First, there's also a const T& operator[](). If youd don't overload it, you'll end up having wrong behaviour if you have vectors in constant objects.
Then, and it's missing above, theres's also an at() which shall be consitent with []
Then you have to take extreme care with the constructors, as there are many of them, to be sure that your arguments will not be misinterpreted.
So you have free functionality, but there's more work ahead than initially thougt. The option of creating your own object with a more limited interface, and a private vector could in the end be a safer approach.
Danger 2:more inconsistency
The vector indexes are vector<T>::size_type. Unfortunately this type is unsigned. The impact of inherit from vector, but redefine operator[] with signed integer indexes has to be carefully analysed. This can lead to subtle bugs according to the way the indexes are defined.
Conclusions:
There's perhap's more work that you think to offer a consistent std::vector interface. So in the end, having your own class using a private vector could be the safer approach.
You should also consider that your code will be maintained one day by people without fortran background, and they might have wrong assumptions about the [] in your code. Is going native c++ really out of question ?
It doesn't seem that bad to just stick with composition, and write wrappers for the member functions you need. There aren't that many. I'd even be tempted to make the array data member public so you can access it directly when needed, although some people would consider that a bigger no-no than inheriting from a base class without a virtual destructor.
template <typename T, size_t N, int start>
class customArray
{
public:
std::array<T,N> data;
T& operator[](int idx) { return data[idx+start]; }
auto begin() { return data.begin(); }
auto begin() const { return data.begin(); }
auto end() { return data.end(); }
auto end() const { return data.end(); }
auto size() const { return data.size(); }
};
int main() {
customArray<int, 7, -3> a;
a.data.fill(5); // can go through the `data` member...
for (int& i : a) // ...or the wrapper functions (begin/end).
cout << i << endl;
}
What are the advantages/disadvantages of the two techniques in comparison ? And more importantly: Why and when should one be used over the other ? Is it just a matter of personal taste/preference ?
To the best of my abilities, I haven't found another post that explicitly addresses my question. Among many questions regarding the actual use of polymorphism and/or type-erasure, the following seems to be closest, or so it seemed, but it doesn't really address my question either:
C++ -& CRTP . Type erasure vs polymorphism
Please, note that I very well understand both techniques. To this end, I provide a simple, self-contained, working example below, which I'm happy to remove, if it is felt unnecessary. However, the example should clarify what the two techniques mean with respect to my question. I'm not interested in discussing nomenclatures. Also, I know the difference between compile- and run-time polymorphism, though I wouldn't consider this to be relevant to the question. Note that my interest is less in performance-differences, if there are any. However, if there was a striking argument for one or the other based on performance, I'd be curious to read it. In particular, I would like to hear about concrete examples (no code) that would really only work with one of the two approaches.
Looking at the example below, one primary difference is the memory-management, which for polymorphism remains on the user-side, and for type-erasure is neatly tucked away requiring some reference-counting (or boost). Having said that, depending on the usage scenarios, the situation might be improved for the polymorphism-example by using smart-pointers with the vector (?), though for arbitrary cases this may very well turn out to be impractical (?). Another aspect, potentially in favor of type-erasure, may be the independence of a common interface, but why exactly would that be an advantage (?).
The code as given below was tested (compiled & run) with MS VisualStudio 2008 by simply putting all of the following code-blocks into a single source-file. It should also compile with gcc on Linux, or so I hope/assume, because I see no reason why not (?) :-) I have split/divided the code here for clarity.
These header-files should be sufficient, right (?).
#include <iostream>
#include <vector>
#include <string>
Simple reference-counting to avoid boost (or other) dependencies. This class is only used in the type-erasure-example below.
class RefCount
{
RefCount( const RefCount& );
RefCount& operator= ( const RefCount& );
int m_refCount;
public:
RefCount() : m_refCount(1) {}
void Increment() { ++m_refCount; }
int Decrement() { return --m_refCount; }
};
This is the simple type-erasure example/illustration. It was copied and modified in part from the following article. Mainly I have tried to make it as clear and straightforward as possible.
http://www.cplusplus.com/articles/oz18T05o/
class Object {
struct ObjectInterface {
virtual ~ObjectInterface() {}
virtual std::string GetSomeText() const = 0;
};
template< typename T > struct ObjectModel : ObjectInterface {
ObjectModel( const T& t ) : m_object( t ) {}
virtual ~ObjectModel() {}
virtual std::string GetSomeText() const { return m_object.GetSomeText(); }
T m_object;
};
void DecrementRefCount() {
if( mp_refCount->Decrement()==0 ) {
delete mp_refCount; delete mp_objectInterface;
mp_refCount = NULL; mp_objectInterface = NULL;
}
}
Object& operator= ( const Object& );
ObjectInterface *mp_objectInterface;
RefCount *mp_refCount;
public:
template< typename T > Object( const T& obj )
: mp_objectInterface( new ObjectModel<T>( obj ) ), mp_refCount( new RefCount ) {}
~Object() { DecrementRefCount(); }
std::string GetSomeText() const { return mp_objectInterface->GetSomeText(); }
Object( const Object &obj ) {
obj.mp_refCount->Increment(); mp_refCount = obj.mp_refCount;
mp_objectInterface = obj.mp_objectInterface;
}
};
struct MyObject1 { std::string GetSomeText() const { return "MyObject1"; } };
struct MyObject2 { std::string GetSomeText() const { return "MyObject2"; } };
void UseTypeErasure() {
typedef std::vector<Object> ObjVect;
typedef ObjVect::const_iterator ObjVectIter;
ObjVect objVect;
objVect.push_back( Object( MyObject1() ) );
objVect.push_back( Object( MyObject2() ) );
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
std::cout << iter->GetSomeText();
}
As far as I'm concerned, this seems to achieve pretty much the same using polymorphism, or maybe not (?).
struct ObjectInterface {
virtual ~ObjectInterface() {}
virtual std::string GetSomeText() const = 0;
};
struct MyObject3 : public ObjectInterface {
std::string GetSomeText() const { return "MyObject3"; } };
struct MyObject4 : public ObjectInterface {
std::string GetSomeText() const { return "MyObject4"; } };
void UsePolymorphism() {
typedef std::vector<ObjectInterface*> ObjVect;
typedef ObjVect::const_iterator ObjVectIter;
ObjVect objVect;
objVect.push_back( new MyObject3 );
objVect.push_back( new MyObject4 );
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
std::cout << (*iter)->GetSomeText();
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
delete *iter;
}
And finally for testing all of the above together.
int main() {
UseTypeErasure();
UsePolymorphism();
return(0);
}
C++ style virtual method based polymorphism:
You have to use classes to hold your data.
Every class has to be built with your particular kind of polymorphism in mind.
Every class has a common binary-level dependency, which restricts how the
compiler creates the instance of each class.
The data you are abstracting must explicitly describe an interface that describes
your needs.
C++ style template based type erasure (with virtual method based polymorphism doing the erasure):
You have to use template to talk about your data.
Each chunk of data you are working on may be completely unrelated to other options.
The type erasure work is done within public header files, which bloats compile time.
Each type erased has its own template instantiated, which can bloat binary size.
The data you are abstracting need not be written as being directly dependent on your needs.
Now, which is better? Well, that depends if the above things are good or bad in your particular situation.
As an explicit example, std::function<...> uses type erasure which allows it to take function pointers, function references, output of a whole pile of template-based functions that generate types at compile time, myraids of functors which have an operator(), and lambdas. All of these types are unrelated to one another. And because they aren't tied to having a virtual operator(), when they are used outside of the std::function context the abstraction they represent can be compiled away. You couldn't do this without type erasure, and you probably wouldn't want to.
On the other hand, just because a class has a method called DoFoo, doesn't mean that they all do the same thing. With polymorphism, it isn't just any DoFoo you are calling, but the DoFoo from a particular interface.
As for your sample code... your GetSomeText should be virtual ... override in the polymorphism case.
There is no need to reference count just because you are using type erasure. There is no need not to use reference counting just because you are using polymorphsm.
Your Object could wrap T*s like how you stored vectors of raw pointers in the other case, with manual destruction of their contents (equivalent to having to call delete). Your Object could wrap a std::shared_ptr<T>, and in the other case you could have vector of std::shared_ptr<T>. Your Object could contain a std::unique_ptr<T>, equivalent to having a vector of std::unique_ptr<T> in the other case. Your Object's ObjectModel could extract copy constructors and assignment operators from the T and expose them to Object, allowing full-on value semantics for your Object, which corresponds to the a vector of T in your polymorphism case.
Here's one view: The question seems to ask how one should choose between late binding ("runtime polymorphism") and early binding ("compile-time polymorphism").
As KerrekSB points out in his comments, there are some things you can do with late binding that it just isn't realistic to do with early binding. Many uses of the Strategy pattern (decoding network I/O) or the Abstract Factory pattern (runtime-selected class factories) fall into this category.
If both approaches are viable, then choosing is a matter of the trade offs involved. In C++ applications, the main tradeoffs I see between early and late binding are implementation maintainability, binary size, and performance.
There are at least some people who feel that C++ templates in any shape or form are impossible to comprehend. Or possibly have some other, less dramatic reservation with templates. C++ templates have many little gotchas ("when do I need to use the 'typename' and 'template' keywords?"), and non-obvious tricks (SFINAE comes to mind).
Another tradeoff is optimization. When you bind early, you give the compiler more information about your program, and so it can (potentially) do a better job optimizing. When you bind late, the compiler (probably) doesn't know ahead of time as much information -- some of that information may be in other compilation units, and so the optimizer can't do as much.
Another tradeoff is program size. In C++ at least, using "compile-time polymorphism" sometimes balloons binary size, as the compiler creates, optimizes, and emits different code for each used specialization. In contrast, when binding late, there's only one code path.
It's interesting to compare the same tradeoff being made in a different context. Take web applications, where one uses (some type of) polymorphism to deal with differences between browsers, and possibly for internationalization (i18n)/localization. Now, a hand-written JavaScript web application would likely use what amounts to late binding here, by having methods which detect capabilities at runtime to figure out what to do. Libraries like jQuery take this tack.
Another approach is to write different code for each possible browser/i18n possibility. While this sounds absurd, it is far from unheard of. The Google Web Toolkit uses this approach. GWT has its "deferred binding" mechanism, used to specialize the compiler's output to different browsers and different localizations. GWT's "deferred binding" mechanism uses early binding: The GWT Java-to-JavaScript compiler figures out all possible ways the polymorphism might be needed, and spits out an entirely different "binary" for each.
The tradeoffs are similar. Wrapping your head around how you extend GWT using deferred binding can be a headache and a half; Having knowledge at compile time allows GWT's compiler to optimize each specialization separately, possibly yielding better performance, and smaller size for each specialization; The whole of a GWT application can end up being many times the size of a comparable jQuery application, due to all of the precompiled specializations.
One benefit to runtime generics that no-one here has mentioned (?) is the possibility for code that is generated and injected into a running application, to use the same List, Hashmap / Dictionary etc. that everything else in that application is already using. Why you'd want to do that, is another question.
There are many reasons not to use typeid. Other than for using members of type_info (implementation defined behavior), it is usually (always?) possible to provide similar functionality using other C++ language features, eg: overloading, virtual functions etc.
So, excluding usage that relies on the implementation defined behavior, does anybody have a real world example where typeid is the best solution?
So, excluding usage that relies on the implementation defined behavior, does anybody have a real world example where typeid is the best solution?
I sometimes use it in debug outputs, to verify that a template argument passed to my function is indeed of a given type. This makes sense in my case since the actual template argument passed to my function is generated by a specialised metafunction and I want to make sure that the right metafunction is used.
boost::any uses typeid to implement any_cast.
template<typename T> any_cast(const any& other) {
if(typeid(T) != other.type()) throw bad_any_cast();
//...actual cast here...
}
You can't be sure T is polymorphic, so dynamic_cast is out of the question, and the enclosed type within the boost::any call is lost by now, so none of the other casts can provide any sort of type safety.
When implementing multimethods (or multiple dispatch), where the actual call is chosen from e.g. a map, using std::type_info* as a key.
Write a dynamic tree where you can on the runtime modify structure of the tree where there are different types in each link, it'll need typeid. dynamic_cast is not enough.
Edit: Here's some details:
class I {
public:
virtual std::string type() const=0;
virtual void *value() const=0;
};
template<class T>
class Impl : public I
{
public:
Impl(T t) : t(t) { }
std::string type() const { return typeid(T).name(); }
void *value() const { return &t; }
private:
T t;
};
And then build a tree out of these:
template<class Node, class Link>
class Tree { };
With link type being the I* interface... Since the above works for any values of type T1,T2,T3,T4, we could also with similar classes for any functions T->T1, T->T2, T->T3, T->T4, and use that function type as the Node of the tree. Now you have proper expressions described in dynamic tree.
You can use typeid to compare the actual types of two objects. Could be useful if you want to check equality of two objects, and first make sure they are of the exact same type (though I must say I haven't seen this done a lot, so there might be a good reason why this is not a good idea...).
I use it to probe the class type of the exception in my catch all handler.
// fudge vtable existence (thrown exceptions must have one)
class StubException
{
virtual ~StubException();
};
.....
catch(...)
{
StubException *e = getExceptionObject(); // compiler/rt specific
std::string s = typeid(e).name();
...
throw;
}
The function getExceptionObject() is part of a small utility library I wrong to access additional information about exceptions. It comes in very handy when a function throws an exception I should be catch but don't. It has definitely saved a lot of frustration over the years since I immediately know the type of exception that needs coverage.
Template methods as in NOT C++ templates.
So, say that you would like to do some searching with different algorithms - Linear and Binary for instance. And you would also like to run those searches through some common routines so that you could, for instance, automatically record the time that a given search took and so on.
The template method pattern fills the bill beautifully. The only problem is that as far as I've managed to dig around, you can't actually implement this behaviour via static methods with C++, 'cause you would also need to make the methods virtual(?) Which is of course a bit of a bummer because I don't have any need to alter the state of the search object. I would just like to pin all the searching-thingies to its own namespace.
So the question is: Would one want to use something like function/method pointers instead? Or would one just use namespaces to do the job?
It's pretty hard to live with this kind of (dare I say) limitations with C++, as something like this would be a breeze with Java.
Edit:
Oh yeah, and since this is a school assignment, the use of external libraries (other than STL) isn't really an option. Sorry for the hassle.
I don't see why you'd need the template method pattern.
Why not just define those algorithms as functors that can be passed to your benchmarking function?
struct BinarySearch { // functor implementing a specific search algorithm
template <typename iter_type>
void operator()(iter_type first, iter_type last){ ....}
};
template <typename data_type, typename search_type>
void BenchmarkSearch(data_type& data, search_type search){ // general benchmarking/bookkeeping function
// init timer
search(data);
// compute elapsed time
}
and then call it like this:
int main(){
std::vector<int> vec;
vec.push_back(43);
vec.push_back(2);
vec.push_back(8);
vec.push_back(13);
BenchmarkSearch(vec, BinarySearch());
BenchmarkSearch(vec, LinearSearch()); // assuming more search algorithms are defined
BenchmarkSearch(vec, SomeOtherSearch();
}
Of course, another approach, which is a bit closer to what you initially wanted, could be to use CRTP (A pretty clever pattern for emulating virtual functions at compile-time - and it works with static methods too):
template <typename T>
struct SearchBase { // base class implementing general bookkeeping
static void Search() {
// do general bookkeeping, initialize timers, whatever you need
T::Search(); // Call derived search function
// Wrap up, do whatever bookkeeping is left
}
}
struct LinearSearch : SearchBase<LinearSearch> // derived class implementing the specific search algorithms
{
static void Search(){
// Perform the actual search
}
};
Then you can call the static functions:
SearchBase<LinearSearch>::Search();
SearchBase<BinarySearch>::Search();
SearchBase<SomeOtherSearch>::Search();
As a final note, it might be worth mentioning that both of these approaches should carry zero overhead. Unlike anything involving virtual functions, the compiler is fully aware of which functions are called here, and can and will inline them, resulting in code that is just as efficient as if you'd hand-coded each case.
Here's a simple templatized version that would work
template <typename F, typename C>
clock_t timer(F f, C c)
{
clock_t begin = clock();
f(c);
return clock() - begin;
}
void mySort(std::vector<int> vec)
{ std::sort(vec.begin(), vec.end()); }
int main()
{
std::vector<int> vec;
std::cout << timer(mySort, vec) << std::endl;
return 0;
}
static doesn't say "I don't need alter an object's state" is says, "I don't need an object". If you need virtual dispatch then you need an object on which to perform virtual dispatch as virtual dispatch is polymorphism based on the runtime type of an object. const would be "I don't need to alter an object's state" and you can have methods which are virtual and const.
You can always implement it the same way you would in Java - pass in an abstract class ISearchable that has a search() method, and override that in LinearSearch and BinarySearch...
You can also use a function pointer (which would be my preffered solution) or a boost::function, or templatize your function and pass in a functor.