Alias Template Specialisation

Alias template specialisation

What the Standard means by "specialization" is the transformation of a generic template to a more specialized entity. For example, instantiating a non-member class template yields a class that's not a template anymore. The term "specialization" is two fold, and can refer to a generated specialization (which is a specialization that was instantiated, possibly from a partial specialization) and to an explicit specialization (which is what you referred to).

Alias templates aren't instantiated and there aren't specializations of them. There is nothing they could instantiate to. Instead, whenever their name is followed by a template argument list, the type denoted is the type you get by replacing the name and argument list by the alias'ed type, replacing all template parameter references with the arguments given in the argument list. That is, rather than the specialization of it being an alias, the alias template itself serves as an alias, without the need to instantiate anything. This replacement is done very early. Consider:

template<typename T> using ref = T&;
template<typename T> void f(ref<T> x) { x = 10; }
int main() { int a; f(a); return a; /* 10 */ }

The replacement is done at the time ref<T> is named (such a names are used to refer to class or function template specializations; hence the spec describes such names to "refer to the specialization of an alias template"). That is, the parameter of f has type T&, and thus, T can be deduced. This property is preventing explicit or partial specializations of alias templates. Because in order to pick the correct specialization of ref, it needs to know T. But to know it, it needs to compare ref<T> against the argument type to deduce T. It's summarized in the paper N1406, "Proposed addition to C++: Typedef Templates", section 2.2

2.2 The Main Choice: Specialization vs. Everything Else

After discussion on the reflectors and in the Evolution WG, it turns out that we have to choose between two mutually exclusive models:

  1. A typedef template is not itself an alias; only the (possibly-specialized) instantiations of the typedef template are aliases. This choice allows us to have specialization of typedef templates.

  2. A typedef template is itself an alias; it cannot be specialized. This choice would allow:

    • deduction on typedef template function parameters (see 2.4)
    • a declaration expressed using typedef templates be the same as the declaration without
      typedef templates (see 2.5)
    • typedef templates to match template template parameters (see 2.6)

It should be noted that the quoted paper, which favors option 1, did not make it into C++0x.


EDIT: Because you desperately want to have a spec quote saying so explicitly. 14.5p3 is one that does

Because an alias-declaration cannot declare a template-id, it is not possible to partially or explicitly specialize an alias template.

Can I specialize a class template with an alias template?

$ clang++ -std=c++0x test.cpp
test.cpp:6:1: error: explicit specialization of alias templates is not permitted
template <>
^~~~~~~~~~~
1 error generated.

Reference: 14.1 [temp.decls]/p3:

3 Because an alias-declaration cannot declare a template-id, it is not
possible to partially or explicitly specialize an alias template.

Template specialization and alias template deduction difference

Why a1 is not deduced in this context?

Because the template argument of doSomething appears in a non-deduced context. An alias template stands almost exactly for what it aliases. And yours is defined as follows:

template<class Category, Category code>
using A = typename AHelper<Category, code>::type;

To deduce code, the compiler will need to deduce something on the left of :: and that is a non-deduced context. Template argument deduction won't even attempt to deduce something if it appears as an argument to the left of the scope resolution operator.

It's not without cause that this is an undeduced context. Remember that templates may be specialized. I could add this:

template<Category code>
struct AHelper<int, code>
{
using type = BImpl<code>; // My own class!
};

A compiler will need to look at all the code in the entire program and try all the types to be sure nothing nefarious is happening for it to be certain that a1 really matches as typename AHelper<Category, code>::type. That's intractable. So a mapping by meta-functions is a one way street only. You can't ask the compiler to deduce the source type (or non-type argument) from the target type.

Partial Specialization of Alias Templates

You can find the answer in the original proposal of alias templates:

2.2 The Main Choice: Specialization vs. Everything Else

After discussion on the reflectors and in the Evolution WG, it turns out that we have to choose between two mutually exclusive models:

  1. A typedef template is not itself an alias; only the (possibly-specialized) instantiations of the typedef template are aliases. This choice allows us to have specialization of typedef templates.

  2. A typedef template is itself an alias; it cannot be specialized. This choice would allow:

    • deduction on typedef template function parameters (see 2.4)
    • a declaration expressed using typedef templates be the same as the declaration without
      typedef templates (see 2.5)
    • typedef templates to match template template parameters (see 2.6)

Alias to a function template specialization

There are some good approaches in the comments: std::bind, using a lambda, passing an instance of F into the function, so I'll provide an alternative.

If the function type that you will pass into foo will always be int(int, int), then you make the template parameter be a non-type template parameter, and then instantiate it with one of your functions:

int f1(int a, int b) { std::cout<<"f1\n"; return a+b; }
int f2(int a, int b) { std::cout<<"f2\n"; return a*b; }

template <int(*F)(int, int)>
void foo(int i, int j)
{
F(i,j);
}

And then call it like so:

int main()
{
constexpr auto foo1 = foo<f1>;
constexpr auto foo2 = foo<f2>;
foo1(1,2);
foo2(1,2);
}

Output:

f1

f2

The trick we're doing here is that we're making the template parameter be a reference to a function, which is hacky but legal AFAIK.

Demo

How do I create specializations of alias templates using class template specialization?

I think you may just want:

template <class >
struct trait;

template <class C, class T>
struct trait<T C::*> {
using class_type = C;
using ret_type = std::invoke_result_t<T C::*, C>;
};

template <typename PMember, typename T = trait<PMember>>
using Index = std::map<typename T::ret_type, typename T::class_type>;

This works for both pointers to member functions and pointers to member data. However, you have to be careful with the member data since ret_type here will always be a reference type. So you may want to do something like:

using ret_type = std::conditional_t<
std::is_function_v<T>,
std::invoke_result_t<T C::*, C>,
T>;

Of course, this is C++17, so you can even side-step the decltype:

template <auto PMember, typename T = trait<decltype(PMember)>>
using Index = std::map<typename T::ret_type, typename T::class_type>;

Index<&Foo::x> indexOnX; // std::map<int, Foo>
Index<&Foo::get_name> indexOnGetName; // std::map<char*, Foo>

using template specialization

No.

But you can define the alias as:

template<typename T>
using YetAnotherVector = typename std::conditional<
std::is_same<T,int>::value,
AFancyIntVector,
std::vector<T>
>::type;

Hope that helps.

Best way (Or workaround) to specialize a template alias

The pattern I use to specialize template aliases (or provide recursive aliases) is to have a corresponding _impl struct. For example:

template <typename T>
struct my_alias_impl { /* def'n */ };

template <>
struct my_alias_impl<int> { /* alternate def'n */ };

template <typename T>
using my_alias = my_alias_impl<T>;

Users would have to specialize on my_alias_impl instead, but the rest of the public interface remains clean.



Related Topics



Leave a reply



Submit