2015-03-18 3 views
2

У меня есть простой базовый класс Shape с его производным классом Ball. Цель состоит в том, чтобы иметь типичность скалярного типа и размеров, следовательно, использование шаблонов.C++ Специализация частичного шаблона по виртуальному методу

#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> circle{{0.2f,0.3f}, 4.0f}; 
    std::cout << circle.volume() << std::endl; 
} 

Я бы хотел использовать частную специализацию по шаблону, чтобы вычислить том в зависимости от размера. Код работает, но является громоздким, если я должен специализироваться на другом типе, таком как double (код будет таким же).

Я знаю, что я не могу иметь частичную шаблонную специализацию для виртуальных методов, но когда я использую комментируемой выше код у меня есть следующие ошибки вместо Ошибки: Шаблон не может быть «виртуальным»:

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> { 

Я действительно не получаю эту ошибку, как ее избежать и какие способы элегантности этой родословности?

ответ

3

Почти всегда, если вам нужен шаблон функции частичной специализации, вы можете использовать «делегат в классе» трюк:

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/3 является 1, так как это число разделение.

Кроме того, чтобы держать это на самом деле тип нейтральный, вы должны отливать константы T:

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

'BallVolume' должны быть объявлены до' Ball' или 'функции члена volume' определено позже –

+0

@ PiotrS. Или читатель применяет здравый смысл при чтении ответа. Я не '# include' заголовок для' std :: size_t' тоже. Но на всякий случай я отменил заказ. – Angew

+0

Спасибо, он отлично работает для меня! Я действительно использовал свой здравый смысл с первого раза;). edit: зачем понадобится static_cast на 3.0? 4.0/3.0 не совсем уверен? – coincoin

Смежные вопросы