虚拟方法的 C++ 部分模板特化

Posted

技术标签:

【中文标题】虚拟方法的 C++ 部分模板特化【英文标题】:C++ Partial template specialization on virtual method 【发布时间】:2015-03-18 09:13:59 【问题描述】:

我有一个简单的基类 Shape 及其派生类 Ball。目的是在标量类型和维度上具有通用性,从而使用模板。

#include <iostream>
#include <array>
#include <cmath>

template<typename T, std::size_t DIM>
class Shape 
public:
    Shape(std::array<T,DIM> base_point) : base_point_(base_point) 
    virtual T volume() const = 0;
protected:
    std::array<T,DIM> base_point_;
;

template<typename T, std::size_t DIM>
class Ball : public Shape<T,DIM> 
public:
    Ball(std::array<T,DIM> base_point, T radius) : Shape<T,DIM>(base_point), radius_(radius) 
    virtual T volume() const;
private:
    T radius_;
;

// Cannot use the generic code below because of Template may not be 'virtual' ?
// template<typename T>
// T Ball<T,2>::volume() const  return M_PI * radius_ * radius_; 

template<>
float Ball<float,2>::volume() const  return M_PI * radius_ * radius_; 

// template<typename T>
// T Ball<T,3>::volume() const  return 4/3 * M_PI * radius_ * radius_ * radius_; 

template<>
float Ball<float,3>::volume() const  return 4/3 * M_PI * radius_ * radius_ * radius_; 

int main() 
    Ball<float,2> circle0.2f,0.3f, 4.0f;
    std::cout << circle.volume() << std::endl;

我想使用部分模板专业化来根据维度计算体积。该代码可以工作,但如果我必须专门针对另一种类型(例如双精度),代码会很麻烦(代码相同)。

我知道我不能对虚拟方法进行部分模板特化,但是当我使用上面的注释代码时,我有以下错误而不是 错误:模板可能不是“虚拟”

shape_generic_naive.cpp:24:25: error: invalid use of incomplete type ‘class Sphere<T, 2ul>’
 T Sphere<T,2>::volume() const return M_PI * radius_ * radius_; 
                         ^
shape_generic_naive.cpp:15:7: error: declaration of ‘class Sphere<T, 2ul>’
 class Sphere : public Shape<T,DIM> 

我并没有真正得到这个错误,如何避免它以及有什么方法可以优雅地获得这种通用性?

【问题讨论】:

【参考方案1】:

几乎总是,如果您需要函数模板部分特化,您可以使用“委托给类”技巧:

template <class T, std::size_t DIM>
struct BallVolume;

template <class T>
struct BallVolume<T, 2>

  static T compute(T radius)  return M_PI * radius * radius; 
;

template <class T>
struct BallVolume<T, 3>

  static T compute(T radius)  return 4.0/3.0 * M_PI * radius * radius * radius; 
;


template<typename T, std::size_t DIM>
class Ball : public Shape<T,DIM> 
public:
    Ball(std::array<T,DIM> base_point, T radius) : Shape<T,DIM>(base_point), radius_(radius) 
    virtual T volume() const  return BallVolume<T, DIM>::compute(radius_); 
private:
    T radius_;
;

请注意,您的 3D 体积公式不正确:4/31,因为它是整数除法。

另外,为了保持真正的类型中立,您应该将常量转换为T

return static_cast<T>(M_PI) * radius * radius;
return 4 / static_cast<T>(3.0) * static_cast<T>(M_PI) * radius * radius * radius;

【讨论】:

BallVolume 应该在Ball 之前声明,或者后面定义的volume 成员函数 @PiotrS。或者读者在阅读答案时运用常识。我也没有#include std::size_t 的标题。但为了以防万一,我把顺序颠倒了。 @coincoin 4.0 的类型为 double。如果你用T = float 实例化它,你会得到一个(合理的)编译器警告。 @coincoin 这看起来真的是一个单独的问题。但考虑到您正在尝试部分特化函数模板Ball::volume,并且不允许函数模板的部分特化。 哦,我明白了,谢谢你的观点:只允许全功能模板专业化。那我会坚持你的解决方案。

以上是关于虚拟方法的 C++ 部分模板特化的主要内容,如果未能解决你的问题,请参考以下文章

C++ 函数模板部分特化?

C++模板—部分特化

C++模板的特化与偏特化

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

使用 STL 容器的部分 C++ 模板特化