Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template specialization on template member of template class

This is probably only a syntax problem.

So i have this template class :

template <typename String, template<class> class Allocator>
class basic_data_object
{
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;
};

And another one :

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
};

Now i want to specialize the second one's T parameter with the first one's inner typedef array_container for any given type.

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::template array_container<T>>
{
};

But this specialization doesn't seem to be matched when i pass an std::vector as the last parameter.

If i create a temporary hard coded typedef:

typedef basic_data_object<std::string, std::allocator<std::string>> data_object;

And use it for the specialization, everything works :

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};

What did i miss ? :)


Alternatively what is the best (smallest / cleanest) way to make this work ?

like image 363
Drax Avatar asked Apr 29 '14 10:04

Drax


People also ask

What is meant by template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is the difference between generic class template and specialization template?

Generics are generic until the types are substituted for them at runtime. Templates are specialized at compile time so they are not still parameterized types at runtime. The common language runtime specifically supports generics in MSIL.

How is a template class different from a template function?

For normal code, you would use a class template when you want to create a class that is parameterised by a type, and a function template when you want to create a function that can operate on many different types.

How many template parameters are allowed in a template classes?

Explanation: Just like normal parameters we can pass more than one or more template parameters to a template class.


1 Answers

The C++ standard says, in [temp.class.spec.match] paragraph 2:

A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list (14.8.2).

14.8.2 is [temp.arg.deduct] i.e. the clause describing template argument deduction for function templates.

If you modify your code to use a similar function template and attempt to call it, you will see that the arguments cannot be deduced:

template <typename String, typename T>
void deduction_test(String,
                    typename basic_data_object<String, std::allocator>::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
}

(I removed the Allocator parameter, since there's no way to pass a template template parameter as a function argument and in the basic_data_object type it's a non-deduced context, I don't believe it affects the result.)

Both clang and GCC say they cannot deduce T here. Therefore the partial specialization will not match the same types used as template arguments.

So I haven't really answered the question yet, only clarified that the reason is in the rules of template argument deduction, and shown an equivalence with deduction in function templates.

In 14.8.2.5 [temp.deduct.type] we get a list of non-deduced contexts that prevent deduction, and the following rule in paragraph 6:

When a type name is specified in a way that includes a non-deduced context, all of the types that comprise that type name are also non-deduced.

Since basic_data_object<String, Allocator> is in a non-deduced context (it is a nested-name-specifier, i.e. appears before ::) that means the type T is also non-deduced, which is exactly what Clang and GCC tell us.


With your temporary hardcoded typedef there is no non-deduced context, and so deduction for T succeeds using the deduction_test function template:

template <typename String, typename T>
void deduction_test(String,
                    typename data_object::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{}); // OK
}

And so, correspondingly, your class template partial specialization can be matched when it uses that type.


I don't see a way to make it work without changing the definition of get_data_object_value, but if that's an option you can remove the need to deduce the array_container type and instead use a trait to detect whether a type is the type you want, and specialize on the result of the trait:

#include <string>
#include <vector>
#include <iostream>

template <typename String, template<class> class Allocator>
class basic_data_object
{
public:
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;

  template<typename T>
    struct is_ac : std::false_type { };

  template<typename T>
    struct is_ac<array_container<T>> : std::true_type { };
};

template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
struct get_data_object_value
{
};

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value<String, Allocator, T, true>
{
  void f() { }
};

int main()
{
  get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
  obj.f();
}

This doesn't really scale if you wanted several class template partial specializations, as you would need to add several bool template parameters with default arguments.

like image 109
Jonathan Wakely Avatar answered Nov 05 '22 16:11

Jonathan Wakely