Template member function specialization in a template class

You need to implement a template class that uses a vector as template parameter. This worked for me.

template<typename T>
class A
{
public:
    T data;

    void print(void) {
        std::cout << "Data output" << std::endl;
    }
    // other functions ...
};

template <typename T>
class A<std::vector<T>>
{
public:
    std::vector<T> data;

    void print() {
        for (auto i : data) {
            std::cout << "Vector output" << std::endl;
        }
    }
};

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)