2011-12-22 49 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_MATERIALVOXEL_HAS_DENSITY部分。两个简单的选项是:

  1. 静态hasMaterial()hasDensity()方法添加到Voxel类,在派生类中重写。
  2. 创建一个类型特征类hasMaterial()hasDensity(),并专门为每个Voxel子类。

使用方法(2)允许为原始类型(int等)定义特征,但在我的情况下这没有用。在这里使用类型特征是否还有其他优点,还是应该采用更简单的静态方法?

注意:我也知道基于SFINAE的方法,我将分别考虑。

编辑1:我已更改示例代码以显示使用模板。我正在寻找这个问题的静态而非运行时解决方案。理想情况下,如果编译器确定它们不能针对给定类型执行,编译器将能够去除if语句中的代码。

+1

“无效过程(体素体素)” - 你的意思是“SomeVoxelSubclass体素”? – Abyx 2011-12-22 09:29:50

+2

你是什么意思“在派生类中重写的静态方法”? – 2011-12-22 09:30:27

+0

感谢您的评论 - 我实际上使用模板并更新了代码示例以反映这一点。不想误导我只是简化了一些代码;-) – PolyVox 2011-12-22 10:01:32

回答

2

类型特征很有用,因为它们可以很容易地添加到类型中,即使您无法更改类型本身。此外,使用类型特征,你可以简单地提供一个合理的默认值(例如,你可以将hasMaterialhasDensity委托给类的合适的静态成员),然后你只需要对不适用于这个类的类进行特化默认。

+0

好的,谢谢,这些都很有用。 – PolyVox 2011-12-22 10:08:04

+0

在这个主题中有很多很好的信息,但是这个答案是最直接回答我的问题的答案。公认。 – PolyVox 2011-12-22 15:28:13

4

为什么这些方法应该是静态的?只需在Voxel类中声明方法virtual bool hasMaterial();virtual bool hasDensity();,并在任何子类中覆盖它们。在子类中返回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() 
    } 
} 

然后,您可以创建一个类似与材料和密度的getter和setter类的接口,也让他们继承。

+0

与静态成员或类型特征相比,这会产生额外的运行时间开销。 – 2011-12-22 09:34:21

+1

是的,但是它说他希望它完全优化? – 2011-12-22 09:35:50

+0

感谢您的评论,但我想要一个编译时解决方案,以便理想情况下,冗余if可以被编译器删除。在这种情况下,性能很重要。我更新了代码以演示在我的代码中使用模板。 – PolyVox 2011-12-22 10:06:19

2

静态成员不能被覆盖。你应该让它们变成虚拟的。我想你的设计有问题,如果可能的话粘贴一些uml图或你的源代码。

+0

我不知道这个限制。我知道静态成员不能是虚拟的,但我不知道你根本无法覆盖它们。我会测试并进一步观察。 – PolyVox 2011-12-22 10:10:00

+0

好吧,事实证明我的意思是'隐藏'而不是'重写'。子类可以通过重新定义它们来隐藏基类中的静态函数。 – PolyVox 2011-12-22 10:39:42

+0

我会建议你避免隐藏方法,这可能会在将来产生很多问题。我仍然认为你的设计中存在某种问题。在这种情况下,UML图表或源代码可能有助于解决您的问题。 – AlexTheo 2011-12-22 11:15:40

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 2011-12-22 10:13:00