Class templates alias should not be visible in generated symbols, do they? - c++

When compiling this snipet in godbolt, most compilers generate two different get methods (different symbol in the assembly window):
template<typename T>
struct Field { T impl; };
template<typename T>
using CurrentField = Field<T>;
template<template <typename> class F>
struct Encompassing { F<int> member; };
auto get(Encompassing<Field> const& instance)
{
return instance.member.impl;
}
auto get(Encompassing<CurrentField> const& instance)
{
return instance.member.impl;
}
I see CurrentField in symbols even if it is an alias. Only gcc complains about redefinition (as expected).
C++ reference on type_alias says
It does not introduce a new type
so I think it is not supposed to behave like this, am I wrong?
Actually most compiler seems to behave like the alias template was substituted with a class Trait like
template<typename T>
struct CurrentField
{
alias type = Field<T> ;
};
Edit:
Here is a more representative example of What I try to achieve on Godbolt. Note that it compiles since there is a single source and no linking but the msvc assembly shows it generated both the pre-instantiated signatures and user calls signatures.
There are 4 parts:
1. a container library with multiple kind of templates like StackField and HeapField,
2. an utility library with member methods like size, with the field as template argument (like the second workaround you proposed),
3. implementations are hidden and pre-instantiated in the c++ for different fields
4. users link their application A and B against this library, using aliases like AField and BField. It works with gcc but link fails in msvc because the signature of my pre-instantiated implementations and the users calls don't match

It is true that an alias template does not introduce a new type. But your class template takes a template as parameter, not a type. Both Field and CurrentField are templates. So this boils down to the question of whether CurrentField should count as the same template as Field or as a separate template. This is CWG issue 1286. See also this thread. GCC follows the first interpretation, clang and MSVC follow the second…
A workaround would seem to be to break the direct mapping of CurrentField to Field by having it go through a helper template:
template <typename T>
struct CurrentFieldHelper { using type = Field<T>; };
template <typename T>
using CurrentField = typename CurrentFieldHelper<T>::type;
working example here
I'm afraid there is no way to achieve the opposite, i.e., make all compilers treat get(Encompassing<CurrentField> const &) as the same function as get(Encompassing<Field> const &). It's hard to suggest a workaround for this particular problem without knowing more about your actual code. A simple solution that might or might not work for your actual code would be to make size a function template that unpacks instance.member and then forwards it to a common function that does the actual work:
template <template <typename> class F>
auto size(Encompassing<F> const& instance)
{
return size(instance.member);
}

Related

Difference between dlang templates and templated classes, structs and functions

I have some trouble understanding templates in D.
I understand what a struct Foo(T) { } or the equivalent for a class or function does, but what is a template Bar(T) { }? how does it differ from a class, struct or function template and when would I use one?
When you see template bar(T), you can think of it as a namespace - sort of like a struct or class. Just like struct Foo(T), the contents are of course templated on the template argument, and are generally only accessible through bar!T.memberName.
I say generally, because there are some special rules. First, we have eponymous templates. If template foo(T) has a member with the exact same name as the template, then foo!T is a synonym for foo!T.foo, and the whole namespace idea sorta disappears. Other members inside foo!T are hidden and inaccessible. In fact, when you write struct Foo(T) {}, the compiler turns it into template Foo(T) { struct Foo {} }.
The other special case is mixin templates, which are basically snippets that will be dropped in verbatim when you instantiate them. That is, this code (note the mixin keyword before template):
mixin template Foo() {
int i;
}
struct Bar {
mixin Foo!();
}
is functionally equivalent to this code:
struct Bar {
int i;
}
Now, why would you use just template? The first is template metaprogramming. If you look at std.traits or std.meta, those modules are filled with templates. Not mixin templates, not templated structs, classes or functions, but just templates. These operate on the values and types passed to them, and returns some kind of value or type.
A very simple example of a template used like this is std.meta.Reverse. It takes a list of arguments and reverses them, and looks like this:
template Reverse(TList...)
{
static if (TList.length <= 1)
{
alias Reverse = TList;
}
else
{
alias Reverse =
AliasSeq!(
Reverse!(TList[$/2 .. $ ]),
Reverse!(TList[ 0 .. $/2]));
}
}
Another case where you'd want to use templates is if your templated type should be elided in some cases. Say you're making your own Nullable(T), and you want Nullable!(Nullable!T) to always be Nullable!T. If you just wrote struct Nullable(T) {}, you would not get this behavior, and you'd end up with double-nullable types. The solution is to use a template constraint:
struct Nullable(T) if (!isNullable!T) {}
and a template to handle the degenerate case:
template Nullable(T) if (isNullable!T) {
alias Nullable = T;
}
I hope this was of some help, and please ask if anything's unclear. :)
Well, technically,
struct S(T)
{
...
}
is equivalent to
template S(T)
{
struct S
{
...
}
}
and
auto foo(T)(T t)
{
...
}
is equivalent to
template foo(T)
{
auto foo(T t)
{
...
}
}
It's just that the shorter syntax is provided to make things cleaner for a common use case. template creates a template for code generation. Nothing within that template exists as real code before the template is instantiated with arguments, and the code that gets generated depends on the template arguments. So, semantic analysis for a template is not done until until it's instantiated.
Part of what's happening with structs, classes, and functions which have the template as part of their declaration instead of explicitly declaring the template to wrap them is what's called an eponymous template. Any template that has a symbol in it with the same name as the template is replaced with that symbol when the template is used. e.g.
template isInt(T)
{
enum isInt = is(T == int);
}
could then be used in an expression such as
auto foo = isInt!int;
and the value of enum isInt.isInt is used in the expression in place of the template. This technique is used heavily with helper templates for template constraints. e.g. isInputRange in
auto foo(R)(R range)
if(isInputRange!R)
{...}
isInputRange is defined as an eponymous template that results in true if the given type is an input range and false otherwise. In a way, it's kind of like having a function for operating on types, though it can operate on values as well, and the result doesn't have to be bool. e.g.
template count(Args...)
{
enum count = Args.length;
}
or
template ArrayOf(T)
{
alias ArrayOf = T[];
}
There's also a shortcut syntax for eponymous templates which aren't user-defined types or functions if they don't have any other members. e.g.
enum count(Args...) = Args.length;
alias ArrayOf(T) = T[];
And as I said, an eponymous template can be a bit like having a function to operate on a type, and that's what they're used for when complicated operations need to be done on types. For instance, using that ArrayOf template with std.meta.staticMap, you can do something like
alias Arrays = staticMap!(ArrayOf, int, float, byte, bool);
static assert(is(Arrays == AliasSeq!(int[], float[], byte[], bool[])));
This can be extremely useful in template constraints (or other eponymous templates to be used in template constraints), or it can be used with something like static foreach to more explicitly generate code. e.g. if I wanted to test some code with all string types, I could write something like
alias Arrays(T) = AliasSeq!(T[],
const(T)[],
const(T[]),
immutable(T)[],
immutable(T[]));
unittest
{
import std.conv : to;
static foreach(S; AliasSeq(Arrays!char, Arrays!wchar, Arrays!dchar))
{{
auto s = to!S("foo");
...
}}
}
These techniques are typically used quite heavily in metaprogramming. They can be used on values in addition to types, but it's usually more efficient to use CTFE for values rather than putting them in an AliasSeq and using various eponymous templates on them. For instance, years ago, Phobos used to have the eponymous template Format which was used to generate strings at compile time similarly to how std.format.format does that at runtime, but once CTFE was improved to the point that format could be used at compile-time, it made no sense to use Format instead of format, because Format was horribly slow in comparison (doing a lot of recursive template instantiations can get to be expensive). So, using template metaprogramming is still required when operating on types, but if you can do what you need to do with CTFE, that's generally better.
If you're looking for metaprogramming tools in Phobos, std.traits and std.meta are the main place to look, though some are scattered throughout Phobos depending on what they're for (e.g. the range-specific ones are in std.range.primitives).
Also, similarly to how you can mixin strings, you can mixin templates. e.g.
template foo(T)
{
T i;
}
void main()
{
mixin foo!int;
auto a = i;
}
So, the code that's generated by the template essentially gets copy-pasted where you mix it in. Optionally, you can put mixin on the template declaration to make it illegal to use it as anything other than a mixin. e.g.
mixin template foo(T)
{
T i;
}
Personally, I usually just use string mixins for that sort of thing, but some folks prefer template mixins.

Where do normal templates end and meta templates begin?

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.

Why do we need to specify the type template parameter for each class function defintion?

Since we define the template type over the class declaration, why do we have to specify it after each function definition? I'm confused because its even in the same file so it seems almost unnecessary to have to specify it over every function and because we are using the :: operator shouldnt it go back to the class declaration and see that T is already defined.
I'm new to c++ and still need to clear up some misunderstandings.
#ifndef __Foo_H__
#define __Foo_H__
template <class T>
class Foobar{
private:
bool foo1(T);
bool foo2(T);
public:
FooBar();
};
template <class T> bool FooBar<T>::foo1(T data){
code..
}
template <class T> bool FooBar<T>::foo2(T data){
code..
}
#endif
First you may rename the argument as for normal function:
template <class U> bool FooBar<U>::foo1(U and_here_too){/**/}
It also manages to handle (partial) specialization:
template <> bool FooBar<int>::foo1(int i){/**/}
template <typename T> bool FooBar<std::vector<T>>::foo1(std::vector<T> v){/**/}
Templates are example of generic programming. The idea is to reuse code/algorithms. In languages with strict type control you come across seemingly unnecessary constraints. For instance you may have some sorting function doing great job in one project but incompatible with types used in another.
C++, C#, and Java introduce generic programming as templates (C++) and generics (C#, Java). In generics (let's talk about Java) classes are existing entities and class parameters serve mainly as type control service. That is their purpose in collections. When you inspect how list works you see list gathers Objects and cast back to the parameterized type only when the object is retrieved. When you write class you can only assume the parameterized type is Object or declared interface like in the following example:
class Test<T extends Comparable> {}
Here you can use T as Comparable. Unless you explicitly declare the interface, the parameter is treated as Object.
Now comes the difference between generics and templates. In C++ you can assume much more about the parameterized type in implementation. You can write sorting of objects of unknown type. In Java you have to at least know what is interface the parameter type. This causes that C++ have to build new class for each parameter (in order to check if the code is correct). Vector<int> **is completely separate type from **Vector<float>. While in Java there exists one class Vector<? extends Comparable>.
:: is scope operator. You can access scope of Vector<int> because the class exists, however, Vector does not.
As a result Java can compile generics and C++ cannot. All templates have to be available in headers to all programmers; you cannot hide it (there is some work to compile templates but I don't know what is its status).
So when you use generics you can refer to method Vector.add() while when templates you have to specify parameter template<class T> Vector<T>.
PS. since template parameter is integral part of class name you may use templates for compile time calculations like fibonaci sequence
template<int N> struct Fibonaci {
static const int element = Fibonacci<N-1>::data + Fibonacci<N-2::data>;
}
template<1> struct Fibonaci {
static const int element = 1;
}
template<0> struct Fibonaci {
static const int element = 0;
}

Template class with no use of template argument

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.

Template Alias with Default Value

Info
I am trying to use a template alias to improve the readabilty of my code. Ideally I would like to the alias to have a default argument such that if I leave out the template it uses the default (exactly with template functions and template classes).
The code would look like
template<typename T = double>
struct mystruct {};
template<typename T = double> using myalias = mystruct<T>;
int main(void) {
myalias MyStructWithDouble; // causes compilation error
myalias<int> MyStructWithInt;
return 0;
}
The compiler (g++ 4.7 in this case) is quite happy with the inclusion of the = double in the alias definition but it seems to then ignore this.
I tried something like "specializing" the alias as well but there the compiler baulked.
Question
Why does the compiler accept the default in the defintion if we are not allowed to use it? Secondly, is there a way of acheiveing this?
Motivation
This example is very simple but in my real code the alias would save a lot of typing (there are more than one template parameter)
Just like with class templates, you still need to supply an empty template argument list:
myalias<> MyStructWithDouble; // compiles