2010-09-14 60 views
3

假设您有一个具有模板长度和类型的向量类 - 即vec<2,float>。这些也可以嵌套 - vec<2,vec<2,vec<2,float> > >vec<2,vec<2,float> >。你可以计算出这些载体之一是如何深嵌套是这样的:C++模板:计算值并在编译时作出决定

​​

麻烦的是,你不会知道它有多深,直到运行时间,但你可能需要知道comile时间深度为了做这样的事情:

// Do this one when depth(v1) > depth(v2) 
template<int N, typename T, int M, typename U> 
inline vec<N,T> operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return v1 + coerce(v2,v1); 
} 
// Do this one when depth(v1) < depth(v2) 
template<int N, typename T, int M, typename U> 
inline vec<M,U> operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return coerce(v1,v2) + v2; 
} 

你不能只是扔在一个“如果”的声明,因为(a),这是更深层次的影响的返回类型和(b)裹胁()生成,如果生成错误您尝试将嵌套向量强制转换为嵌套较少的向量。

是否有可能做这样的事情,还是我推高了C++模板的限制?

+0

如果'coerce'能够在编译时检查,您可能会在那里找到您的答案! – 2010-09-14 21:05:24

+2

C++模板的限制。哈。 – GManNickG 2010-09-14 21:08:01

+2

C++模板没有限制。 – 2010-09-14 21:11:32

回答

5

完全有可能。尝试例如

template<int N, typename T, int M, typename U> 
inline typename enable_if<is_deeper<T, U>::value, vec<N,T> >::type 
operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return v1 + coerce(v2,v1); 
} 

template<int N, typename T, int M, typename U> 
inline typename enable_if<is_deeper<U, T>::value, vec<M,U> >::type 
operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return coerce(v1,v2) + v2; 
} 

哪里is_deeper是一样的东西

/* BTW what do you want to do if none is deeper? */ 
template<typename T, typename U> 
struct is_deeper { static bool const value = false; }; 

template<typename T, int N, typename U> 
struct is_deeper<vec<N, U>, T> { 
    static bool const value = true; 
}; 

template<typename T, int N, typename U> 
struct is_deeper<T, vec<N, U> > { 
    static bool const value = false; 
}; 

template<typename T, int N, int M, typename U> 
struct is_deeper<vec<M, T>, vec<N, U> > : is_deeper<T, U> 
{ }; 
+2

有时我使用'mpl :: true_'作为'struct is_deeper ,T>:mpl :: true_ {};'和false同样 – Anycorn 2010-09-14 21:10:59

+0

绝对的辉煌!谢谢! (顺便说一句,如果没有更深的 - 它们是相同的深度 - 它会解决到一个更专业的+运营商,增加每个组件) – Chris 2010-09-14 22:26:45

+1

应避免二元类型的特征,因为它们不适用于memoization。这将导致O(深度)新模板为每对比较的阵列类型生成,这可能会上升到O(深度^ 3)。最好定义一个“深度”特征并比较结果。 – Potatoswatter 2010-09-14 22:38:28

3

模板元编程会让你自由。我在运行时这样做,但它会在编译时进行计算:

#include <iostream> 
#include <boost\static_assert.hpp> 
using namespace std; 

template<size_t Depth> class Vec 
{ 
public: 
enum {MyDepth = Vec<Depth-1>::MyDepth + 1}; 
}; 

template<> class Vec<1> 
{ 
public: 
enum {MyDepth = 1}; 
}; 

    BOOST_STATIC_ASSERT(Vec<12>::MyDepth == 12); 
// Un-commenting the following line will generate a compile-time error 
// BOOST_STATIC_ASSERT(Vec<48>::MyDepth == 12); 

int main() 
{ 
cout << "v12 depth = " << Vec<12>::MyDepth; 
} 

编辑:包括提升静态断言来演示如何,这是在编译时进行评估。

2

部分专业化对反思非常有用。通常最好避免使用编译时常量结果的inline函数。 (C++ 0x中可能会缓解这个有点,但我不知道有多少。)

首先,你vec模板看起来很像boost::array/std::tr1::array/std::array,所以我只是把它array

template< class ArrT > 
struct array_depth; // in the general case, array depth is undefined 

template< class ElemT, size_t N > // partial specialization 
struct array_depth< array< ElemT, N > > { // arrays do have depth 
    enum { value = 0 }; // in the general case, it is zero 
}; 

template< class ElemT, size_t N1, size_t N2 > // more specialized than previous 
struct array_depth< array< array< ElemT, N1 >, N2 > { 
    enum { value = 1 + array_depth< array< ElemT, N1 > >::value }; // recurse 
}; 

// define specializations for other nested datatypes, C-style arrays, etc. 
// C++0x std::rank<> already defines this for C-style arrays 
+0

这是一个很好的解决方案。对于C++ 03或C++ 0x,使用'static'类成员/联合之间是否有区别? – Chubsdad 2010-09-15 00:26:20

+0

@Chubsdad我不认为C++ 0x已经改变了任何'静态'语义。我更喜欢把'enum'放在'static const int'上面,因为你不必在类范围外定义它。嗯,实际上是的,C++ 0x将类内初始化扩展到了其他类型......但是它仍然有点没有意义,因为仍然需要命名空间范围的定义。 – Potatoswatter 2010-09-15 01:45:55

+0

@Chubsdad如果你只是从静态成员读取数据,并且永远不依赖它的地址(通过获取左值并将其存储到某个引用或显式地获取其地址),则不需要定义静态数据成员。一个类内初始化的const整数就足够了(这种静态数据成员不被ODR认为是“使用”的)。有一些边缘案例会使它“使用”,这可能令人惊讶 - 例如在“a?对于具有相同类型的两个左值,“lval1:lval2”“使用”lval的指示对象。但这种情况很少见。 – 2010-09-15 05:24:37