模板类中的模板成员函数特化

kfc*_*ckf 15 c++ templates member-functions template-specialization c++17

我有一个模板类和一个成员函数print()来打印数据。

template<typename T>
class A
{
public:
   T data;
   void print(void) 
   { 
      std::cout << data << std::endl; 
   }
   // other functions ...
};
Run Code Online (Sandbox Code Playgroud)

然后,我想打印标量数据或矢量数据,所以我给出了一个专门的定义并得到一个编译器错误。

template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
   for (const auto& d : data) 
   {
      std::cout << d << std::endl;
   }
}
Run Code Online (Sandbox Code Playgroud)

问题:为什么这个成员函数特化会报错?为向量定义打印函数的正确方法是什么?

解决方案 1:我已经测试了以下定义。

template<typename T>
class A<std::vector<T>>
{
public:
   std::vector<T> data;
   void print(void) {   // OK
      // ... 
   } 
}
Run Code Online (Sandbox Code Playgroud)

这个有效,但我必须将其他成员函数复制到这个专门的类中。


编辑

解决方案2:为了防止复制所有其他成员函数,我定义了一个包含公共成员函数的基类并从基类继承:

template<typename T>
class Base
{
public:
   T data;
   // other functions ...
};

template<typename T>
class A : public Base<T>
{
public:
   void print(void) 
   {
      std::cout << this->data << std::endl;
   }
};

template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
   void print(void) 
   {
      for (const auto& d : this->data)
      {
         std::cout << d << std::endl;
      }
   }
};
Run Code Online (Sandbox Code Playgroud)

此解决方案效果很好。有没有更好或更传统的解决方案?

JeJ*_*eJo 10

为什么这个成员函数专业化会出错?

A例如A<std::vector<int>>,当您实例化模板类时,模板参数T等于std::vector<int>,不是std::vector<T>,这是函数的特化情况。不幸的是,这不能用评论中提到的成员函数来完成


有没有更好的解决方案?

是的;在您可以使用if constexprtrait 来检查std::vector,就像这样。

#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;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

见在线直播

这样你只保留一个模板类,并且print()T在编译时根据模板类型实例化适当的部分。


如果您无权访问 C++17,另一种选择是SFINAE成员(自)。

#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;
    }
};
Run Code Online (Sandbox Code Playgroud)

见在线直播


如果我有更多其他数据类型,例如自定义向量类或矩阵,该怎么办?我必须定义很多is_xx_vector吗?

您可以检查类型是所提供的类型的特化,如下所示。通过这种方式,您可以避免为每种类型提供许多特征。该is_specialization基本上是从这个帖子的启发

#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 {};
Run Code Online (Sandbox Code Playgroud)

print函数可能在

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;
   }
}
Run Code Online (Sandbox Code Playgroud)

见在线直播


Ali*_*zna 5

您需要实现一个使用向量作为模板参数的模板类。这对我有用。

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;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 如果除了 `print` 之外你还有 10 个成员函数怎么办? (4认同)
  • 不,在某些情况下这可能是一个很好的解决方案。例如,如果您有许多类似“print”的函数。那么在单独的类中实现它们并使用完全专业化是合理的。 (4认同)