2010-09-14 37 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