Here is a simple code in C++:
#include <iostream>
#include <typeinfo>
template<typename T>
void function()
{
std::cout << typeid(T).name() << std::endl;
}
int main()
{
function<int>();
function<double>();
return 0;
}
I have read that templates in C++ is a compile-time feature, which is not like generics in C#/Java.
So as I understood, the C++ compiler will divide a single defined function into various number (depends on calls count with different type) of functions.
Am I right or not? I'm not an expert in C++ compilers, so I'm asking a piece of advice from you.
If my suggestion about compiler output is correct, I want to know if I can describe the code above as static polymorphism?
Because it seems to be not overriding, but just calling a copy from executable or... it doesn't matter what does application have in output binary image, but only the important part is in C++ code level and I musn't look at how does compiler produce an output.
Is there real static polymorhism in C++?
Absolutely - there are three mechanisms for static polymorphism: templates, macros and function overloading.
So as I understood, the C++ compiler will divide a single defined function into various number (depends on calls count with different type) of functions. Am I right or not?
That's the general idea. The number of functions that get instantiated depends on the number of permutations of template parameters, which may be explicitly specified as in function<int> and function<double> or - for templates that use the template parameters to match function arguments - automatically derived from the function arguments, for example:
template <typename T, size_t N>
void f(T (&array)[N])
{ }
double d[2];
f(d); // instantiates/uses f<double, 2>()
You should end up with a single copy of each instantiated template in the executable binary image.
I want to know if I can describe the code above as static polymorphism?
Not really.
template<> function is instantiated for two types
crucially, polymorphism is not used to choose which of the two instantiations of function to dispatch to at the call sites
trivially, during such instantiations typeid(T) is evaluated for int and double and effectively behaves polymorphically from a programmer perspective (it's a compiler keyword though - implementation unknown)
trivially, a mix of static and nominally dynamic (but here likely optimisable to static) polymorphism supports your use of std::cout
Background - polymorphism and code generation
The requirement I consider crucial for polymorphism is:
when code is compiled (be it "normal" code or per template instantiation or macro substitution), the compiler automatically chooses (creates if necessary) - and either inlines or calls - distinct type-appropriate behaviour (machine code)
i.e. code selection/creation is done by the compiler based only on the type(s) of variable(s) involved, rather than being explicitly hard-coded by the programmer's choice between distinct function names / instantiations each only capable of handling one type or permutation of types
for example, std::cout << x; polymorphically invokes different code as the type of x is varied but still outputs x's value, whereas the non-polymorphic printf("%d", x) handles ints but needs to be manually modified to printf("%c", x); if x becomes a char.
But, what we're trying to achieve with polymorphism is a bit more general:
reuse of algorithmic code for multiple types of data without embedding explicit type-detection and branching code
that is, without the program source code containing if (type == X) f1(x) else f2(x);-style code
reduced maintenance burden as after explicitly changing a variable's type fewer consequent changes need to be manually made throughout the source code
These bigger-picture aspects are supported in C++ as follows:
instantiation of the same source code to generate distinct behaviours (machine code) for some other type or permutation of types (this is an aspect of parametric polymorphism),
actually known as "instantiation" for templates and "substitution" for preprocessor macros, but I'll use "instantiation" hereafter for convenience; conceptually, re-compilation or re-interpretation...
implicit dispatch (static or dynamic) to distinct behaviour (machine code) appropriate to the distinct type(s) of data being processed.
...and in some minor ways per my answer at Polymorphism in c++
Different types of polymorphism involve either or both of these:
dispatch (2) can happen during instantiation (1) for templates and preprocessor macros,
instantiation (1) normally happens during dispatch (2) for templates (with no matching full specialisation) and function-like macros (kind of cyclic, though macros don't expand recursively)
dispatch (2) can be happen without instantiation (1) when the compiler selects a pre-existing function overload or template specialisation, or when the compiler triggers virtual/dynamic dispatch.
What does your code actually use?
function<int> and function<double> reuse the function template code to create distinct code for each of those types, so you are getting instantiation (1) as above. But, you are hard-coding which instantiation to call rather than having the compiler implicitly select an instantiation based on the type of some parameter, i.e. so you don't directly utilise implicit dispatch ala (2) when calling function. Indeed, function lacks a parameter that the compiler could use for implicit selection of a template instantiation.
Instantiation (1) alone is not enough to consider your code to have used polymorphism. Still, you've achieved convenient code re-use.
So what would be unambiguously polymorphic?
To illustrate how templates can support dispatch (2) as well as instantiation (1) and unarguably provide "polymorphism", consider:
template<typename T>
void function(T t)
{
std::cout << typeid(T).name() << std::endl;
}
function(4); // note: int argument, use function<int>(...)
function(12.3); // note: double argument, use function<double>(...)
The above code also utilises the implicit dispatch to type-appropriate code - aspect "2." above - of polymorphism.
Non type parameters
Interestingly, C++ provides the ability to instantiate templates with integral parameters such as boolean, int and pointer constants, and use them for all manner of things without varying your data types, and therefore without any polymorphism involved. Macros are even more flexible.
Note that using a template in a C.R.T.P. style is NOT a requirement for static polymorphism - it's an example application thereof. During instantiation, the compiler exhibits static polymorphism when matching operations to implementations in the parameter-specified type.
Discussion on terminology
Getting a definitive definition of polymorphism is difficult. wikipedia quotes Bjarne Stroustrup's online Glossary "providing a single interface to entities of different types": this implies struct X { void f(); }; struct Y { void f(); }; already manifests polymorphism, but IMHO we only get polymorphism when we use the correspondence of interface from client code, e.g. template <typename T> void poly(T& t) { t.f(); } requires static polymorphic dispatch to t.f() for each instantiation.
Wikipedia lists three types of polymorphism:
If a function denotes different and potentially heterogeneous implementations depending on a limited range of individually specified
types and combinations, it is called ad hoc polymorphism. Ad hoc
polymorphism is supported in many languages using function
overloading.
If the code is written without mention of any specific type and thus can be used transparently with any number of new types, it is
called parametric polymorphism. In the object-oriented programming
community, this is often known as generics or generic programming. In
the functional programming community, this is often simply called
polymorphism.
Subtyping (or inclusion polymorphism) is a concept wherein a name may denote instances of many different classes as long as they are
related by some common superclass. In object-oriented programming,
this is often referred to simply as polymorphism.
The first one refers to function overloading. The third type refers to late binding or runtime polymorphism, the kind you would see for example in inheritance. The second one is what we're interested in.
Templates are a compile-time construct and type deduction is a process when the compiler automatically figures out the template arguments. This is where static polymorphism comes in.
For example:
template <typename T, typename U>
auto func(const T& t, const U& u) -> decltype(t + u)
{
return (t + u);
}
This will work for any two types with compatible plus operators. There's no need to specify the template argument if the compiler can figure it out. It would be ad hoc polymorphism if you wrote function overloads that performed different behavior, for example string concatenation vs integer addition.
However, in your example, you have instantiations for your functions that are distinct, function<int> and function<double>. Here's a quote:
To be polymorphic, [a()] must be able to operate with values of at
least two distinct types (e.g. int and double), finding and executing
type-appropriate code.
In that case, the instantiations are specific for the type in which they were instantiated, so there is no polymorphism involved.
There is no static polymorphism in your example because there is no polymorphism. This is because function<int>() does not look the same as function<double>().
Examples of static polymorphism would include simple function overloading, function templates that can work with type deduction, type traits, and the curiously recurring template pattern (CRTP). So this variation on your example would qualify as static polymorphism:
#include <iostream>
#include <typeinfo>
template<typename T>
void function(T)
{
std::cout << typeid(T).name() << std::endl;
}
int main()
{
function(0); // T is int
function(0.0); // T is double
return 0;
}
Here is another example:
template<typename T>
void function(T t)
{
t.foo();
}
struct Foo()
{
void foo() const {}
};
struct Bar()
{
void foo() const {}
};
int main()
{
Foo f;
Bar b;
function(f); // T is Foo
function(b); // T is Bar
}
For c++ the term 'static polymorphism' is normally used for e.g. the CRTP type design patterns:
template<typename Derived>
class Base
{
void someFunc() {
static_cast<Derived*>(this)->someOtherFunc();
};
};
class ADerived : public Base<ADerived>
{
void someOtherFunc() {
// ...
}
};
It generally means that types and inheritance constraints are deduced and verified at compile/link time. The compiler will emit error messages if operations are missing or invalid on the specified types. In that sense it's not really polymorphism.
While it can be argued that the example in the OP does not exhibit static polymorphism, the use of specialization can make a more compelling case:
template<class T>
class Base
{
public:
int a() { return 7; }
};
template<>
class Base<int>
{
public:
int a() { return 42; }
};
template<>
class Base<double>
{
public:
int a() { return 121; }
};
We see here that for most classes a() will return 7; The specialized (derived) instanciations for int and double can have radically different behaviors, demonstrated in the simple case by the differing return values, the same can be done for templates with for example int parameters, and can exhibit what can be oddly termed as static recursive polymoprphism.
While the term polymorphic is possibly being stretched, the concept is definately there. What is missing is not the ability to redefine functions, but the ability for specialized classes to automatically inherit functions that are not changing behavior.
Related
I'm working on a program where some data is statically allocated and some is dynamically allocated. Now I want to have another type that can be called with any template of the type as its argument.
#include <array>
#include <vector>
template <int size> class Foo {
std::array<int, size> data;
public:
int& operator[](std::size_t idx) {return data[idx];}
};
template <> class Foo<-1> {
std::vector<int> data;
public:
int& operator[](std::size_t idx) {return data[idx];}
};
// option 1- polymorphism
struct FooCaller {
virtual void operator()(Foo data) = 0; // how would I make this work with both forms of Foo?
};
// option 2- generic programming
template <class T> concept CanCallFoo = requires (const T& t) {
t(std::declval<Foo&>()); // how do I ensure that this can call any overload of Foo?
};
Both methods would be fine, but I'm not sure how to go about this. Because the full code is more complex, I'd rather not have both Foos inherit from a base.
A callable F could write a restriction that it can be called by Foo<x> such that an arbitrary function of x must be true to be valid.
In order for your "can be called with any Foo" test to work, you would have to invert an arbitrary function at compile time.
There is no practical way to do this short of examinjng all 2^32 possible values of x. No, really. The problem you have is that the type F is possibly too powerful for you to determine its properties. This is related to Halt and Rice's theorem and the fact that template metaprogramming and C++ overload resolution is Turing complete (only related, because 2^32 is finite).
In the other case, you could type erase. Write a type RefAnyFoo/AnyFooValue that can be constructed from any Foo type and "type erases" the operations you want, like how std::function<void(int)> type erases. It could either duck type the Foo or actually restrict to instsnces of types made from the template.
Then your interface is one that takes a AnyFooValue/RefAnyFoo (depending if you are talking about copies or references).
Now, flipping this over, you can write a concept that accepts F that take RefFooAny. This isn't quite what you asked for, but plausibly you have an X/Y problem; you had a real problem, came up with incomplete solutions, then asked about how to get your solutions working, instead of the original problem.
Similarly, it is possible you only care about certain ducktype properties of Foo<?> and not the specific type. Then a concept that checks those ducktypes could be used by your callable; this again deosn't solve your problem, as much as it flips it upside down, because you cannot verify a callable accepts an entire concept from outside the callable.
Often when writing templated code, I find myself needing to store an instance of the template type in a member variable. For example, I might need to cache a value to be used later on. I would like to be able to write my code as:
struct Foo
{
template<typename T>
T member;
template<typename T>
void setMember(T value)
{
member<T> = value;
}
template<typename T>
T getMember()
{
return member<T>;
}
};
Where members are specialized as they are used. My question:
Is such templated member variable possible with current C++ generative coding facilities?
If not, are there any proposals for such a language feature?
If not, are there any technical reasons why such a thing is not possible?
It should be obvious that I do not want to list all possible types (e.g. in a std::variant) as that is not generative programming and would not be possible if the user of the library is not the same as the author.
Edit: I think this somewhat answers my 3rd question from above. The reason being that today's compilers are not able to postpone instantiation of objects to after the whole program has been parsed:
https://stackoverflow.com/a/27709454/3847255
This is possible in the library by combining existing facilities.
The simplest implementation would be
std::unordered_map<std::type_index, std::any>
This is mildly inefficient since it stores each std::type_index object twice (once in the key and once inside each std::any), so a std::unordered_set<std::any> with custom transparent hash and comparator would be more efficient; this would be more work though.
Example.
As you say, the user of the library may not be the same as the author; in particular, the destructor of Foo does not know which types were set, but it must locate those objects and call their destructors, noting that the set of types used may be different between instances of Foo, so this information must be stored in a runtime container within Foo.
If you're wary about the RTTI overhead implied by std::type_index and std::any, we can replace them with lower-level equivalents. For std::type_index you can use a pointer to a static tag variable template instantiation (or any similar facility), and for std::any you can use a type-erased std::unique_ptr<void, void(*)(void*)> where the deleter is a function pointer:
using ErasedPtr = std::unique_ptr<void, void(*)(void*)>;
std::unordered_map<void*, ErasedPtr> member;
struct tag {};
template<class T> inline static tag type_tag;
member.insert_or_assign(&type_tag<T>, ErasedPtr{new T(value), [](void* p) {
delete static_cast<T*>(p);
}});
Example. Note that once you make the deleter of std::unique_ptr a function pointer, it is no longer default-constructible, so we can't use operator[] any more but must use insert_or_assign and find. (Again, we've got the same DRY violation / inefficiency, since the deleter could be used as the key into the map; exploiting this is left as an exercise for the reader.)
Is such templated member variable possible with current C++ generative coding facilities?
No, not exactly what you describe. What is possible is to make the enclosing class a template and use the template parameters to describe the types of the class' members.
template< typename T >
struct Foo
{
T member;
void setMember(T value)
{
member = value;
}
T getMember()
{
return member;
}
};
In C++14 and later, there are variable templates, but you can't make a template non-static data member of a class.
If not, are there any proposals for such a language feature?
Not that I know of.
If not, are there any technical reasons why such a thing is not possible?
The primary reason is that that would make it impossible to define binary representation of the class. As opposed to templates, a class is a type, which means its representation must be fixed, meaning that at any place in the program Foo and Foo::member must mean the same things - same types, same object sizes and binary layout, and so on. A template, on the other hand, is not a type (or, in case of variable templates, is not an object). It becomes one when it is instantiated, and each template instantiation is a separate type (in case of variable templates - object).
I am trying to get my head around applying template programming (and at some future point, template metaprogramming) to real-world scenarios. One problem I am finding is that C++ Templates and Polymorphism don't always play together the way I want.
My question is if the way I'm trying to apply template programming is improper (and I should use plain old OOP) or if I'm still stuck in the OOP mindset.
In this particular case, I am trying to solve a problem using the strategy-pattern. I keep running into the problem where I end up wanting something to behave polymorphically which templates don't seem to support.
OOP Code using composition:
class Interpolator {
public:
Interpolator(ICacheStrategy* const c, IDataSource* const d);
Value GetValue(const double);
}
void main(...) {
Interpolator* i;
if (param == 1)
i = new Interpolator(new InMemoryStrategy(...), new TextFileDataSource(...));
else if (param == 2)
i = new Interpolator(new InMemoryStrategy(...), new OdbcDataSource(...));
else if (param == 3)
i = new Interpolator(new NoCachingStrategy(...), new RestDataSource(...));
while (run) {
double input = WaitForRequest();
SendRequest(i->GetValue(input));
}
}
Potential Template Version:
class Interpolator<class TCacheStrategy, class TDataSource> {
public:
Interpolator();
Value GetValue(const double); // may not be the best way but
void ConfigCache(const& ConfigObject); // just to illustrate Cache/DS
void ConfigDataSource(const& ConfigObject); // need to configured
}
//Possible way of doing main?
void main(...) {
if(param == 1)
DoIt(Interpolator<InMemoryStrategy, TextFileDataSource>(), c, d);
else if(param == 2)
DoIt(Interpolator<InMemoryStrategy, OdbcDataSource>(), c, d)
else if(param == 3)
DoIt(Interpolator<NoCachingStrategy, RestDataSource>(), c, d)
}
template<class T>
void DoIt(const T& t, ConfigObject c, ConfigObject d) {
t.ConfigCache(c);
t.ConfigDataSource(c);
while(run) {
double input = WaitForRequest();
SendRequest(t.GetValue(input));
}
}
When I try to convert the OOP implementation to a template-based implementation, the Interpolator code can be translated without a lot of pain. Basically, replace the "interfaces" with Template type parameters, and add a mechanism to either pass in an instance of Strategy/DataSource or configuration parameters.
But when I get down to the "main", it's not clear to me how that should be written to take advantage of templates in the style of template meta programming. I often want to use polymorphism, but it doesn't seem to play well with templates (at times, it feels like I need Java's type-erasure generics... ugh).
When I often find I want to do is have something like TemplateType<?, ?> x = new TemplateType<X, Y>() where x doesn't care what X, Y is.
In fact, this is often my problem when using templates.
Do I need to apply one more level of
templates?
Am I trying to use my shiny new power template wrench to
install a OOP nail into a PCI slot?
Or am I just thinking of this all
wrong when it comes to template
programming?
[Edit] A few folks have pointed out this is not actually template metaprogramming so I've reworded the question slightly. Perhaps that's part of the problem--I have yet grok what TMP really is.
Templates provide static polymorphism: you specify a template parameter at compile time implementing the strategy. They don't provide dynamic polymorphism, where you supply an object at runtime with virtual member functions that implement the strategy.
Your example template code will create three different classes, each of which contains all the Interpolator code, compiled using different template parameters and possibly inlining code from them. That probably isn't what you want from the POV of code size, although there's nothing categorically wrong with it. Supposing that you were optimising to avoid function call overhead, then it might be an improvement on dynamic polymorphism. More likely it's overkill. If you want to use the strategy pattern dynamically, then you don't need templates, just make virtual calls where relevant.
You can't have a variable of type MyTemplate<?> (except appearing in another template before it's instantiated). MyTemplate<X> and MyTemplate<Y> are completely unrelated classes (even if X and Y are related), which perhaps just so happen to have similar functions if they're instantiated from the same template (which they needn't be - one might be a specialisation). Even if they are, if the template parameter is involved in the signatures of any of the member functions, then those functions aren't the same, they just have the same names. So from the POV of dynamic polymorphism, instances of the same template are in the same position as any two classes - they can only play if you give them a common base class with some virtual member functions.
So, you could define a common base class:
class InterpolatorInterface {
public:
virtual Value GetValue(const double) = 0;
virtual void ConfigCache(const& ConfigObject) = 0;
virtual void ConfigDataSource(const& ConfigObject) = 0;
virtual ~InterpolatorInterface() {}
};
Then:
template <typename TCacheStrategy, typename TDataSource>
class Interpolator: public InterpolatorInterface {
...
};
Now you're using templates to create your different kinds of Interpolator according to what's known at compile time (so calls from the interpolator to the strategies are non-virtual), and you're using dynamic polymorphism to treat them the same even though you don't know until runtime which one you want (so calls from the client to the interpolator are virtual). You just have to remember that the two are pretty much completely independent techniques, and the decisions where to use each are pretty much unrelated.
Btw, this isn't template meta-programming, it's just using templates.
Edit. As for what TMP is, here's the canonical introductory example:
#include <iostream>
template<int N>
struct Factorial {
static const int value = N*Factorial<N-1>::value;
};
template<>
struct Factorial<0> {
static const int value = 1;
};
int main() {
std::cout << "12! = " << Factorial<12>::value << "\n";
}
Observe that 12! has been calculated by the compiler, and is a compile-time constant. This is exciting because it turns out that the C++ template system is a Turing-complete programming language, which the C preprocessor is not. Subject to resource limits, you can do arbitrary computations at compile time, avoiding runtime overhead in situations where you know the inputs at compile time. Templates can manipulate their template parameters like a functional language, and template parameters can be integers or types. Or functions, although those can't be "called" at compile time. Or other templates, although those can't be "returned" as static members of a struct.
I find templates and polymorphism work well toegther. In your example, if the client code doesn't care what template parameters Interpolator is using then introduce an abstract base class which the template sub-classes. E.g.:
class Interpolator
{
public:
virtual Value GetValue (const double) = 0;
};
template<class TCacheStrategy, class TDataSource>
class InterpolatorImpl : public Interpolator
{
public:
InterpolatorImpl ();
Value GetValue(const double);
};
void main()
{
int param = 1;
Interpolator* interpolator = 0;
if (param==1)
interpolator = new InterpolatorImpl<InMemoryStrategy,TextFileDataSource> ();
else if (param==2)
interpolator = new InterpolatorImpl<InMemoryStrategy,OdbcDataSource> ();
else if (param==3)
interpolator = new InterpolatorImpl<NoCachingStrategy,RestDataSource> ();
while (true)
{
double input = WaitForRequest();
SendRequest( interpolator->GetValue (input));
}
}
I use this idiom quite a lot. It quite nicely hides the templatey stuff from client code.
Note, i'm not sure this use of templates really classes as "meta-programming" though. I usually reserve that grandiose term for the use of more sophisticated compile-time template tricks, esp the use of conditionals, recursive defintions etc to effectively compute stuff at compile time.
Templates are sometimes called static (or compile-time) polymorphism, so yes, they can sometimes be used instead of OOP (dynamic) polymorphism. Of course, it requires the types to be determined at compile-time, rather than runtime, so it can't completely replace dynamic polymorphism.
When I often find I want to do is have something like TemplateType x = new TemplateType() where x doesn't care what X,Y is.
Yeah, that's not possible. You have to do something similar to what you have with the DoIt() function. Often, I think that ends up a cleaner solution anyway (you end up with smaller functions that do just one thing each -- usually a good thing). But if the types are only determined at runtime (as with i in the OOP version of your main function), then templates won't work.
But In this case, I think your template version solves the problem well, and is a nice solution in its own right. (Although as onebyone mentions, it does mean code gets instantiated for all three templates, which might in some cases be a problem)
Jörg's answer to this question nicely delineates between "normal" templates (what the question refers to, perhaps erroneously, as generics) which operate on data and meta templates which operate on a program. Jörg then wisely mentions that programs are data so its really all one and the same. That said, meta-templates are still a different beast. Where do normal templates end and meta templates begin?
The best test I can come up with is if a template's arguments are exclusively class or typename the template is "normal" and meta otherwise. Is this test correct?
The boundary: Signature with Logical Behaviour
Well, in my opinion the boundary-line is to be drawn where a template's signature stops to be a simple signature yielding runtime-code and becomes a definition of explicit or implicit logic, which will be executed/resolved at compile-time.
Some examples and explanation
Regular Templates, i.e. with only typename, class or possibly value-type template parameters, produce executable cpp code, once instantiated during compile time.
The code is (important) not executed at compile time
E.g. (very simple and most likely unrealistic example, but explains the concept):
template<typename T>
T add(const T& lhs, const T& rhs) {
return(lhs + rhs);
}
template<>
std::string add<std::string>(
const std::string& lhs,
const std::string& rhs) {
return (lhs.append(rhs));
}
int main() {
double result = add(1.0, 2.0); // 3.0
std::string s = add("This is ", " the template specialization...");
}
Once compiled, the root-template will be used to instantiate the above code for the type double, but will not execute it.
In addition, the specialization-template will be instantiated for the text-concatenation, but also: not executed at compile time.
This example, however:
#include <iostream>
#include <string>
#include <type_traits>
class INPCWithVoice {
void doSpeak() { ; }
};
class DefaultNPCWithVoice
: public INPCWithVoice {
public:
inline std::string doSpeak() {
return "I'm so default, it hurts... But at least I can speak...";
}
};
class SpecialSnowflake
: public INPCWithVoice {
public:
inline std::string doSpeak() {
return "WEEEEEEEEEEEH~";
}
};
class DefaultNPCWithoutVoice {
public:
inline std::string doSpeak() {
return "[...]";
}
};
template <typename TNPC>
static inline void speak(
typename std::enable_if<std::is_base_of<INPCWithVoice, TNPC>::value, TNPC>::type& npc)
{
std::cout << npc.doSpeak() << std::endl;
};
int main()
{
DefaultNPCWithVoice npc0 = DefaultNPCWithVoice();
SpecialSnowflake npc1 = SpecialSnowflake();
DefaultNPCWithoutVoice npc2 = DefaultNPCWithoutVoice();
speak<DefaultNPCWithVoice>(npc0);
speak<SpecialSnowflake>(npc1);
// speak<DefaultNPCWithoutVoice>(npc2); // Won't compile, since DefaultNPCWithoutVoice does not derive from INPCWithVoice
}
This sample shows template meta programming (and in fact a simple sample...).
What happens here, is that the 'speak'-function has a templated parameter, which is resolved at compile time and decays to TNPC, if the type passed for it is derived from INPCWithVoice.
This in turn means, if it doesn't, the template will not have a candidate for instantiation and the compilation already fails.
Look up SFINAE for this technique: http://eli.thegreenplace.net/2014/sfinae-and-enable_if/
At this point there's some logic executed at compile time and the entire program, will be fully resolved once linked to the executable/library
Another very good example is: https://akrzemi1.wordpress.com/2012/03/19/meta-functions-in-c11/
Here you can see a template meta programming implementation of the factorial-function, demonstrating, that even the bytecode can be entirely equal to a fixed-value use, if the meta-template decays to a constant.
Finalizing example: Fibonacci
#include <iostream>
#include <string>
#include <type_traits>
template <intmax_t N>
static unsigned int fibonacci() {
return fibonacci<N - 1>() + fibonacci<N - 2>();
}
template <>
unsigned int fibonacci<1>() {
return 1;
}
template <>
unsigned int fibonacci<2>() {
return fibonacci<1>();
}
template <intmax_t MAX>
static void Loop() {
std::cout << "Fibonacci at " << MAX << ": " << fibonacci<MAX>() << std::endl;
Loop<MAX - 1>();
}
template <>
void Loop<0>() {
std::cout << "End" << std::endl;
}
int main()
{
Loop<10>();
}
This code implements scalar template argument only template meta programming for the fibonacci-sequence at position N.
In addition, it shows a compile-time for loop counting from 10 to 0!
Finally
I hope this clarifies things a bit.
Remember though: The loop and fibonacci examples instantiate the above templates for each index!!!
Consequently, there's a horrible amount of redundancy and binary bloat!!!
I'm not the expert myself and I'm sure there's a template meta programming kung fu master on stackoverflow, who can append any necessary information missing.
Attempt to differentiate and define the terms
Let's first try to roughly define the terms. I start with a hopefully good enough definition of "programming", and then repeatedly apply the "usual" meaning of meta- to it:
programming
Programming results in a program that transforms some data.
int add(int value) { return value + 42; }
I just wrote code that will result in a program which transforms some data - an integer - to some other data.
templates (meta programming)
Meta programming results in a "program" that transforms some program into another. With C++ templates, there's no tangible "program", it's an implicit part of the compiler's doings.
template<typename T>
std::pair<T,T> two_of_them(T thing) {
return std::make_pair(thing, thing);
}
I just wrote code to instruct the compiler to behave like a program that emits (code for) another program.
meta templates (meta meta programming?)
Writing a meta template results in a ""program"" that results in a "program" which results in a program. Thus, in C++, writing code that results in new templates. (From another answer of me:)
// map :: ([T] -> T) -> (T -> T) -> ([T] -> T)
// "List" "Mapping" result "type" (also a "List")
// --------------------------------------------------------
template<template<typename...> class List,
template<typename> class Mapping>
struct map {
template<typename... Elements>
using type = List<typename Mapping<Elements>::type...>;
};
That's a description of how the compiler can transform two given templates into a new template.
Possible objection
Looking at the other answers, one could argue that my example of meta programming is not "real" meta programming but rather "generic programming" because it does not implement any logic at the "meta" level. But then, can the example given for programming be considered "real" programming? It does not implement any logic either, it's a simple mapping from data to data, just as the meta programming example implements a simple mapping from code (auto p = two_of_them(42);) to code (the template "filled" with the correct type).
Thus, IMO, adding conditionals (via specialization for example) just makes a template more complex, but does not change it's nature.
Your test
Definitively no. Consider:
template<typename X>
struct foo {
template<typename Y>
using type = X;
};
foo is a template with a single typename parameter, but "results" in a template (named foo::type ... just for consistency) that "results" - no matter what parameter is given - to the type given to foo (and thus to the behavior, the program implemented by that type).
Let me start answering using a definition from dictionary.com
Definition
meta -
a prefix added to the name of a subject and designating another subject that analyzes the original one but at a more abstract, higher level: metaphilosophy; metalinguistics.
a prefix added to the name of something that consciously references or comments upon its own subject or features: a meta-painting of an
artist painting a canvas.
Template programming is formost used as a way to express relations in the type system of C++. I would argue it is therefore fair to say that template programming inherently makes use of the type system itself.
From this angle of perspective, we can rather directly apply the definition given above. The difference between template programming and meta (template-)programming lies the treatment of template arguments and the intended result.
Template code that inspects its arguments clearly falls into the former defintion while the creation of new types from template arguments arguably falls into the later. Note that this must also be combined with the intent of your code to operate on types.
Examples
Let's take a look at some examples:
Implementation of std::aligned_storage;
template<std::size_t Len, std::size_t Align /* default alignment not implemented */>
struct aligned_storage {
typedef struct {
alignas(Align) unsigned char data[Len];
} type;
};
This code fulfills the second condition, the type std::aligned_storage is used to create another type. We could make this ever clearer by creating a wrapper
template<typename T>
using storage_of = std::aligned_storage<sizeof(T), alignof(T)>::type;
Now we fulfill both of the above, we inspect the argument type T, to extract its size and aligment, then we use that information to construct a new type dependent on our argument. This clearly constitutes meta-programming.
The original std::aligned_storage is less clear but still quite pervasive. We provide a result in the form of a type, and both of the arguments are used to create a new type. The inspection arguably happens when the internal array type of type::data is create.
A counter examples for completeness of the argument:
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue { /*Implementation defined implementation*/ };
Here, you might have the question:
But doesn't priority queue also do type inspection, for example to retrieve the underlying Container, or to assess the type of its iterators?
And yes it does, but the goal is different. The type std::priority_queue itself does not constitute meta template programming, since it doesn't make use of the information to operate within the type system. Meanwhile the following would be meta template programming:
template<typename C>
using PriorityQueue = std::priority_queue<C>;
The intent here is to provide a type, not the operations on the data themselves. This gets clearer when we look at the changes we can make to each code.
We can change the implementation of std::priority_queue maybe to change the permitted operations. For example to support a faster access, additional operations or compact storage of the bits inside the container. But all of that is entirely for the actual runtime-functionality and not concerned with the type system.
In contrast look at what we can do to PriotityQueue. If we were to choose a different underlying implementation, for example if we found that we like Boost.Heap better or that we link against Qt anyways and want to choose their implementation, that's a single line change. This is what meta programming for, we make choices within the type system based arguments formed by other types.
(Meta-)Template signatures
Regarding your test, as we have seen above, storage_of has exclusively typename arguments but is very clearly meta programming. If you dig deaper, you will find that the type system itself is, with templates, Turing-complete. Without even needing to explicitely state any integral variables, we could for example easily replace them by recursively stacked templates (i.e. Zermelo construction of the natural numbers)
using Z = void;
template<typename> struct Zermelo;
template<typename N> using Successor = Zermelo<N>;
A better test in my eyes would be to ask if the given implementation has runtime effects. If a template struct or alias does not contain any definition with an effect only happening at runtime, it's probably template meta programming.
Closing words
Of course normal template programming might utilize meta template programming. You can use meta template programming to determine properties of normal template arguments.
For example you might choose different output strategies (assuming some meta-programming implementation of template<class Iterator> struct is_pointer_like;
template<class It> generateSomeData(It outputIterator) {
if constexpr(is_pointer_like<outputIterator>::value) {
generateFastIntoBuffer(static_cast<typename It::pointer> (std::addressof(*outputIterator));
} else {
generateOneByOne(outputIterator);
}
}
This constitutes template programming employing the feature implemented with meta template programming.
Where do normal templates end and meta templates begin?
When the code generated by templates rely on the fundamental aspects of programming, such as branching and looping, you have crossed the line from normal templates to template meta programming.
Following the description from the article you linked:
A regular function
bool greater(int a, int b)
{
return (a > b);
}
A regular function that works with only one type (ignoring implicit conversions for the time being).
A function template (generic programming)
template <typename T>
bool greater(T a, T b)
{
return (a > b);
}
By using a function template, you have created generic code that can be applied to many types. However, depending on its usage, it may not be correct for null terminated C strings.
Template Metaprogramming
// Generic implementation
template <typename T>
struct greater_helper
{
bool operator(T a, T b) const
{
return (a > b);
}
};
template <typename T>
bool greater(T a, T b)
{
return greater_helper<T>().(a > b);
}
// Specialization for char const*
template <>
struct greater_helper<char const*>
{
bool operator(char const* a, char const* b) const
{
return (strcmp(a, b) > 0);
}
};
Here, you have written code as if to say:
If T is char const*, use a special function.
For all other values of T, use the generic function.
Now you have crosses the threshold of normal templates to template metaprogramming. You have introduced the notion if-else branching using templates.
I have stumbled many times on classes defined like
class PureVirtualClass
{
virtual int foo() = 0;
virtual bool bar() = 0;
}
template <class T> class ImplClass : public virtual PureVirtualClass
{
virtual ~ImplClass(){};
int foo() { return 42;}
bool bar() { return true;}
//several other method having nothing to do with T
}
This "design" appears so often I want to think the original developer knew what he was doing by defining ImplClass as template class but without any reference to the template argument T anywhere. My own c++ template knowledge is kinda limited.
Is there a benefit to this or is it just a confused programmer?
There can be a benefit for classes being templated but not depending on the argument. Most often you see such things to define (empty) tag-structures for template metaprogramming:
template <class X>
struct some_tag {};
The benefit of classes like yours in general is that while you have the same functionality in each class, they are different classes and you can't copy one into the other, i.e. an object of type ImplClass<int> is not compatible with another object of type ImplCalss<float>.
There are many useful cases of the idea mentioned by Arne. For instance, looking at Very basic tuple implementation, this is how a single tuple element is defined:
template <size_t N, typename T>
class TupleElem
{
T elem;
public:
T& get() { return elem; }
const T& get() const { return elem; }
};
It is templated on N, without depending on it. Why? Because the tuple implementation
template <size_t... N, typename... T>
class TupleImpl <sizes <N...>, T...> : TupleElem <N, T>...
{
//..
};
derives multiple such elements, each with a unique N, serving as an identifier. Without it, TupleImpl would be deriving the same class twice, had two element types been identical within parameter pack T.... Neither random access to elements would work in this case (via an explicit call of function get() of the appropriate TupleElem base class, which would be ambiguous), nor empty base optimization (via specializing TupleElem for empty types T to not have a data member of type T).
This is a real use case, and exactly how std::tuple is implemented by clang. Of course, a class like TupleElem would be a hidden implementation detail, and not part of the interface. For instance, gcc follows an entirely different recursive class design.
In general, you will need to study the context where classes are used to understand the intent of the designer.
maybe that developer simply is too lazy to split the classes into .h and .cpp files?
Without using templates, linker errors would occur if the classes are used in multiple compilations units. When using templates, the linker usually discards duplicate instantiations of a template at link time (or handles the problem in a different way).
While this may be an answer to "why did the developer do this", I would not recommend this if the question was "when should I introduce template arguments which are never used" (see the other answers for this). Even though it is annoying to split code into .h and .cpp (especially when used to languages like Java or C#), it's the usual C++ way. And it is definitely easier to read/understand than using templates only for this purpose. Also, it makes the use of the classes less readable.