简体   繁体   English

模板成员 function 模板专业化 class

[英]Template member function specialization in a template class

I have a template class and a member function print() to print the data.我有一个模板 class 和一个成员 function print()来打印数据。

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

Then, I want to either print scalar data or vector data, so I give a specialized definition and get a compiler error.然后,我想打印标量数据或向量数据,所以我给出了一个专门的定义并得到一个编译器错误。

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

Question : Why does this member function specialization get an error?:为什么这个成员 function 专业化会出错? What is the correct way to define a print function for a vector?为矢量定义打印 function 的正确方法是什么?

Solution 1 : I have tested the following definition.解决方案 1 :我已经测试了以下定义。

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

This one worked, but I have to copy the other member functions into this specialized class.这个可行,但我必须将其他成员函数复制到这个专门的 class 中。


EDIT :编辑

Solution 2 : To prevent copy all the other member functions, I define a base class containing the common member functions and inherit from the base class:解决方案 2 :为了防止复制所有其他成员函数,我定义了一个包含通用成员函数的基础 class 并从基础 class 继承:

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;
      }
   }
};

This solution works well.这个解决方案效果很好。 Are there some better or more conventional solutions?有没有更好或更传统的解决方案?

Why does this member function specialization get error?为什么这个成员 function 专业化会出错?

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.当您实例化模板 class A例如A<std::vector<int>>时,模板参数T等于std::vector<int> ,而不是std::vector<T> ,这是function。 Unfortunately this can not be done with member functions as mentioned in the comments .不幸的是,这不能用评论中提到的成员函数来完成


Are there some better solutions?有没有更好的解决方案?

Yes ;是的 In you could use if constexpr with a trait to check the std::vector , like this.,您可以使用带有特征的if constexpr来检查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;
        }
    }
};

( 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.这样你只保留一个模板 class 并且print()将在编译时根据模板类型T实例化适当的部分。


If you don not have access to C++17, other option is to SFINAE the members(Since ).如果您无权访问 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;
    }
};

( 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 ?我必须定义很多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 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 {};

And the print function could be in :并且print function 可以在中:

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 ) 见在线直播

You need to implement a template class that uses a vector as template parameter.您需要实现一个使用向量作为模板参数的模板 class。 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;
        }
    }
};

You could always use named tag dispatching to check if type provided by template user is vector.您始终可以使用命名标记调度来检查模板用户提供的类型是否为矢量。

A<std::vector<T>> notation won't work as you both try to take into account that T is type and vector of types which is contradicting with itself. A<std::vector<T>> 表示法不起作用,因为你们都试图考虑到 T 是类型和与自身相矛盾的类型的向量。

Below is code I used named tag dispatching as solution to your problem:下面是我使用命名标签调度作为解决您的问题的代码:

#include <iostream>
#include <vector>
#include <type_traits>

using namespace std;

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

template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};

template<typename T>
class A
{
public:
    T data;
    void print(std::true_type) {
        for (auto& a : data) { std::cout << a << std::endl; } 
    }
    void print(std::false_type) {
        std::cout << data << std::endl;
    }
    void print() {
        print(is_vector<T>{});
    }
};
    
int main()
{
    A<int> a;
    a.data = 1;
    a.print();
    A<std::vector<int>> b;
    b.data = { 1, 2 ,3 ,4 ,5 };
    b.print();
    return 0;
}

Succesfully compiled with https://www.onlinegdb.com/online_c++_compiler使用https 成功编译://www.onlinegdb.com/online_c++_compiler

Based on answer: Check at compile-time is a template type a vector基于答案: 在编译时检查是模板类型向量

You can dispatch printing to another member function ( static or not).您可以将打印发送给另一个成员 function( static与否)。 For example:例如:

template<typename T>
class A {
public:
    T data;
    
    void print() const {
        print_impl(data);
    }
    
private:
    template<class S>
    static void print_impl(const S& data) {
        std::cout << data;
    }

    template<class S, class A>
    static void print_impl(const std::vector<S, A>& data) {
        for (const auto& d : data)
            std::cout << d;
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM