Explicit Specialization of Template Class Member Function

explicit specialization of template class member function

It doesn't work that way. You would need to say the following, but it is not correct

template <class C> template<>
void X<C>::get_as<double>()
{

}

Explicitly specialized members need their surrounding class templates to be explicitly specialized as well. So you need to say the following, which would only specialize the member for X<int>.

template <> template<>
void X<int>::get_as<double>()
{

}

If you want to keep the surrounding template unspecialized, you have several choices. I prefer overloads

template <class C> class X
{
template<typename T> struct type { };

public:
template <class T> void get_as() {
get_as(type<T>());
}

private:
template<typename T> void get_as(type<T>) {

}

void get_as(type<double>) {

}
};

Template member specialization in template class

If we look at n4810 § 13.8.3


  1. A member function, a member function template, a member class, a
    member enumeration, a member class template, a static data member, or
    a static data member template of a class template may be explicitly
    specialized for a class specialization that is implicitly
    instantiated; in this case, the definition of the class template shall
    precede the explicit specialization for the member of the class
    template. If such an explicit specialization for the member of a class
    template names an implicitly-declared special member function
    (11.3.3), the program is ill-formed.

You are however allowed to do this, where both are specialized:

template<typename U>
struct A
{
template<typename... Ts> auto func() { /* ... */ }
};

template <>
template <>
auto A<int>::func<int>()
{
// ok, enclosing class template explicitly specialized
}

While this is invalid c++:

template <typename U>
template <>
auto A<U>::func<int>()
{
// ops: invalid, enclosing class template not explicitly specialized
}

According to:


  1. In an explicit specialization declaration for a member of a class
    template or a member template that appears in namespace scope, the
    member template and some of its enclosing class templates may remain
    unspecialized, except that the declaration shall not explicitly
    specialize a class member template if its enclosing class templates
    are not explicitly specialized as well.

And because of:


  1. A declaration of a function template, class template, or variable
    template being explicitly specialized shall precede the declaration of
    the explicit specialization.

Therefore this should not be inside the outer template declaration:

template <typename U>
struct A {
template<typename...Ts> auto func();
template<> auto func<int>() { return std::string{"foo"}; } // should not work
};

I do not know why clang permits this.

But it is allowed to be in the namespace scope where the primary template was declared, in this case global namespace scope.

explicit specialization of template class member function

Unfortunately a template method inside a template class cannot be specialized just based on template argument of method.

You need to specialize the template class also. In other words, the member method specialization should be a complete specialization with respect to class template (i.e. <T,U>) params as well as the member template params (i.e. <size_t>).

For example, you may have to specialize something like this (demo):

template<>  // <------ you have to specialize class also
template<>
inline void A<int, double>::BindValues<1>() // <-------- see A<T,U>
{
...
}

Explicit specialization of member function template in source file

Declaring specializations in a source file and can cause all sorts of subtle issues that are very difficult to diagnose. The compiler isn't obligated to help you in any regard here either. The standard strongly encourages you not to do this, with the help of a limerick, in [temp.expl.spec]/6-7:

If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and either the specialization is used in a way
that would cause an implicit instantiation to take place or the member is a virtual member function, the
program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit
specialization that is declared but not defined.

The placement of explicit specialization declarations for function templates, class templates, variable templates,
member functions of class templates, [...], etc., can affect whether a program is well-formed according
to the relative positioning of the explicit specialization declarations and their points of instantiation
in the translation unit as specified above and below. When writing a specialization, be careful about its
location; or to make it compile will be such a trial as to kindle its self-immolation
.

It's likely that in some translation units, the specialization happened to be declared before the first use - and in some translation units it hasn't been. It's better to avoid all such issues entirely by simply declaring your specialization in your header:

// writer.h
class Writer {
public:
...
template <typename T, typename V>
void addField(const std::string& name, V v)
{ /* ... */ }
};

// still writer.h
template <>
inline void Writer::addField<some_type, int>(const std::string& name, int v)
{ /* ... */ }

You can also just declare it in the header (no longer needs to be inline), and still define it in the source.

Template member function specialization in a template class

Why does this member function specialization get error?

When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.



Are there some better solutions?

Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.

#include <type_traits> // std::false_type, std::true_type
#include <vector>

// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};

template<typename T>
class A /* final */
{
T mData;

public:
// ...constructor

void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};

(See Live Online)

This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.


If you don not have access to C++17, other option is to SFINAE the members(Since c++11).

#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>

// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};

template<typename T>
class A /* final */
{
T mData;

public:
// ...constructor

template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}

template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};

(See Live Online)



What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?

You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post

#include <type_traits> // std::false_type, std::true_type
#include <vector>

// custom MyVector (An example)
template<typename T> struct MyVector {};

template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};

template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};

And the print function could be in c++17:

void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}

(See Live Online)

How do I define an explicit specialization of a template member function in .cpp file

I'm not sure, but I think you're trying to do this.

In Foo.h

struct Color {};

class Foo
{
public:
template<typename T> int add(T b);
};

// declare, but do not implement, specialization
template<>
int Foo::add<Color>(Color c);

// general implementation
template<typename T>
int Foo::add(T b)
{
return 0;
}

In Foo.cpp

#include "Foo.h"

// explicitly implement
template<>
int Foo::add<Color>(Color b)
{
return 42;
}

Finally, main.cpp

#include "Foo.h"

int main()
{
Foo f;
std::cout << f.add(Color()) << '\n';
}

Output

42

template class - member function specialization

Not compliantly, it doesn't.

FWIW, GCC 4.8 rejects your code without the template <>.

Your compiler is either buggy or has an extension to support this; I can confirm that MSVS 2012 accepts the code. I'm told that MSVS 2013 November CTP also eats it up without complaint. To be fair, Visual Studio was always fairly lenient about template specifications.

[C++11: 14.7/3]: An explicit specialization may be declared for a function template, a class template, a member of a class template or a member template. An explicit specialization declaration is introduced by template<>. [..]

The only exception to this rule is:

[C++11: 14.7.3/5]: [..] Members of an explicitly specialized class template are
defined in the same manner as members of normal classes, and not using the template<> syntax. [..]

… but that does not apply here.

How to specialize template member function?

As Igor mentioned, you can implement the generic version in the header file, and then the specialization in the cpp file, for example:

// MyStruct.h

struct MyStruct {
// ...
template <typename T>
void readField(std::istream& in, T& data) {
read(in, data);
data = ntohl(data);
}
};

Then in the cpp file you can implement the specialization, for example:

// MyStruct.cpp

template <>
void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data) {
read(in, data);
}

Update: After reading the comments, the specialization can also be in the same header file as the primary template, but not within the struct, for example (I verified this by compiling and running a similar example without error):

// MyStruct.h

struct MyStruct {
// ...
template <typename T>
void readField(std::istream& in, T& data) {
read(in, data);
data = ntohl(data);
}
};

template <>
inline void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data) {
read(in, data);
}

// End MyStruct.h


Related Topics



Leave a reply



Submit