2011-12-22 2 views
3

У меня есть класс (Voxel) с подклассами, который может иметь или не иметь несколько различных свойств (материал, плотность и т. Д.) С помощью методов get и set. Теперь я хочу, чтобы написать код следующим образом:Преимущества характеристик типа против статических элементов?

template <typename VoxelType> 
void process(VoxelType voxel) 
{ 
    if(VOXEL_HAS_MATERIAL) 
    { 
    //Do some work which involves calling get/setMaterial() 
    } 
    if(VOXEL_HAS_DENSITY) 
    { 
    //Do some work which involves calling get/setDensity() 
    } 
} 

Поэтому я бы хотел реализовать VOXEL_HAS_MATERIAL и VOXEL_HAS_DENSITY частей. Два простых варианта:

  1. Добавить статические hasMaterial() и hasDensity() методы к Voxel класса, должны быть переопределены в производных классах.
  2. Создайте класс черт типа с hasMaterial() и hasDensity() и специализируйте его для каждого подкласса Voxel.

Использование метода (2) позволяет определять признаки для примитивных типов (int и т. Д.), Но это не полезно в моем случае. Существуют ли какие-либо дополнительные преимущества для использования признаков типа здесь или я должен использовать более простой метод статического метода?

Примечание: Я также знаю подходы, основанные на SFINAE, которые я буду рассматривать отдельно.

Редактировать 1: Я изменил образец кода, чтобы показать использование шаблонов. Я ищу статические, а не временные решения этой проблемы. В идеале, компилятор сможет отключить код в операторах if, если он определяет, что они не могут быть выполнены для данного типа.

+1

"пустота процесса (Voxel вокселя)" - вы имеете в виду "SomeVoxelSubclass вокселя"? – Abyx

+2

Что значит «статические методы, подлежащие переопределению в производных классах»? –

+0

Спасибо за комментарии. Я действительно использую шаблоны и обновил образец кода, чтобы отразить это. Не хотел вводить в заблуждение, я просто немного упростил код :-) – PolyVox

ответ

2

Черты типа полезны, потому что их можно легко добавить к типам, даже если вы не можете изменить сам тип. Кроме того, используя черты типа, вы можете просто обеспечить разумное значение по умолчанию (например, вы можете просто делегировать hasMaterial и hasDensity соответствующим статическим членам класса), а затем вам просто нужно специализировать признак для классов, которые не работают с этим по умолчанию.

+0

Хорошо, спасибо, это полезные моменты. – PolyVox

+0

Много хорошей информации в этой теме, но этот ответ тот, который наиболее непосредственно отвечает на мой вопрос. Принято. – PolyVox

4

Почему эти методы должны быть статическими? Просто объявите методы как virtual bool hasMaterial(); и virtual bool hasDensity(); в классе Voxel и переопределите их в любых подклассах. Возвращайте true в подклассах, если они есть, и false, если нет.

Вы могли бы сделать:

void process(Voxel* voxel) 
{ 
    if(voxel->hasMaterial()) 
    { 
     //Do some work which involves calling get/setMaterial() 
    } 
    if(voxel->hasDensity()) 
    { 
     //Do some work which involves calling get/setDensity() 
    } 
} 

Затем вы можете создать интерфейс, как класса с добытчиками и сеттеров материала и плотности, а также их наследуют это.

+0

Это создает дополнительные затраты времени выполнения по сравнению со статическими элементами или типами. –

+1

Да, но где он говорит, что хочет, чтобы он был полностью оптимизирован? –

+0

Спасибо за комментарий, но я хотел бы решение для компиляции, чтобы в идеале избыточное if's могло быть удалено компилятором. В этом случае важна производительность. Я обновил код, чтобы продемонстрировать использование шаблонов в моем коде. – PolyVox

2

Статические элементы не могут быть переопределены. Вместо этого вы должны сделать их виртуальными. Я предполагаю, что у вас возникла проблема с дизайном, если это возможно, вставьте некоторую диаграмму uml или исходный код.

+0

Я не знал об этом ограничении. Я знаю, что статические члены не могут быть виртуальными, но я не знал, что вы не можете их переопределить. Я буду проверять и смотреть на это дальше. – PolyVox

+0

Хорошо, оказывается, я имел в виду «скрывать», а не «переопределять». Подкласс может скрывать статические функции в базовом классе, переопределяя их. – PolyVox

+0

Я предлагаю вам избегать скрытых методов, это может вызвать много проблем в будущем. Я все еще верю, что у вас есть какая-то проблема в вашем дизайне. UML-диаграмма или исходный код в этом случае могут быть полезны для решения вашей проблемы. – AlexTheo

1

Вместо использования времени выполнения используйте статическую проверку.

Во-первых, определить этот макрос:

#define HAS_MEMBER_VARIABLE(NEW_STRUCT, VAR)         \ 
template<typename T> struct NEW_STRUCT {          \ 
    struct Fallback { int VAR; }; /* introduce member name "VAR" */    \ 
    struct Derived : T, Fallback { };           \ 
                       \ 
    template<typename C, C> struct ChT;           \ 
                       \ 
    template<typename C> static char (&f(ChT<int Fallback::*, &C::VAR>*))[1]; \ 
    template<typename C> static char (&f(...))[2];        \ 
                       \ 
    static bool const value = sizeof(f<Derived>(0)) == 2;      \ 
}; 

который проверить, существует ли переменная-член в классе.

Затем используйте SFINAE, чтобы сделать что-то, если переменная существует, как в следующем примере:

#include <iostream> 

#define HAS_MEMBER_VARIABLE(NEW_STRUCT, VAR)         \ 
template<typename T> struct NEW_STRUCT {          \ 
    struct Fallback { int VAR; }; /* introduce member name "VAR" */    \ 
    struct Derived : T, Fallback { };           \ 
                       \ 
    template<typename C, C> struct ChT;           \ 
                       \ 
    template<typename C> static char (&f(ChT<int Fallback::*, &C::VAR>*))[1]; \ 
    template<typename C> static char (&f(...))[2];        \ 
                       \ 
    static bool const value = sizeof(f<Derived>(0)) == 2;      \ 
}; 

HAS_MEMBER_VARIABLE(x_check, x) 

struct A 
{ 
    int x; 
}; 
struct B 
{ 
    float notX; 
}; 

template< typename T, bool hasX = x_check<T>::value > 
struct doX 
{ 
    static void foo(const T & t) 
    { 
     std::cout<<"type has x variable, and it's value is "<<t.x<<std::endl; 
    } 
}; 
template< typename T > 
struct doX< T, false > 
{ 
    static void foo(const T &) 
    { 
     std::cout<<"type has no x variable"<<std::endl; 
    } 
}; 

template< typename T > 
void doFoo(const T& t) 
{ 
    doX< T, x_check<T>::value >::foo(t); 
}; 

int main() 
{ 
    A a; a.x = 6; 
    B b; b.notX = 3.6; 

    std::cout<<"Calling foo() on A : "; 
    doFoo(a); 
    std::cout<<"Calling foo() on B : "; 
    doFoo(b); 
} 
+0

Это хороший ответ, но я сказал, что знаю о подходах на основе SFINAE и буду рассматривать их отдельно. Я буду отмечать как полезный, но не могу принять за правильный ответ. – PolyVox

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