2012-12-11 36 views
15

這樣的事情有可能存在嗎?是否有可能在C++中開發靜態循環?

template<int Channel> 
void deduce_mask(Matrix const &src, int mask[]) 
{ 
    //I hope i could become a constant and the compiler would unroll the loop at compile time   
    for(int i = Channel; i != -1; --i) 
    {    
     //mapper is a helper class which translate two and three dimension into one dimension index 
     //constexpr makes it possible to find out the index at compile time 
     mask[mapper(0, 1, i)] = src(row - 1, col)[i]; 
     mask[mapper(1, 1, i)] = src(row, col)[i]; 
     mask[mapper(2, 1, i)] = src(row + 1, col)[i];  
    } 
} 

代替

template<int Channel> 
class deduceMask 
{ 
public: 
    static void deduce_mask(matrix const &src, int mask[]); 
}; 

template<int Channel> 
void deduce_mask(matrix const &src, int mask[]) 
{     
    mask[mapper(0, 1, Channel)] = src(row - 1, col)[Channel]; 
    mask[mapper(1, 1, Channel)] = src(row, col)[Channel]; 
    mask[mapper(2, 1, Channel)] = src(row + 1, col)[Channel];  

    deduceMask<Channel - 1>::deduce_mask(src, mask); 
} 

template<> 
class deduceMask<-1> 
{ 
public: 
    static void deduce_mask(matrix const &src, int mask[]) 
    { 

    } 
}; 

第二種解決方案是唯一的解決辦法我能想出的時候我想的編譯器來找出 在編譯time.Do結果我有一個簡單的方法來使「i」變成恆定值,如 元編程解決方案?對我來說,一個簡單的for循環更容易處理,而不是元編程版本的 。

對不起,我可憐的英語,我希望我能正確解釋我的問題。

+2

你也可以遞歸地編寫它並使用constexpr,如果你喜歡這種類型的語法? – Agentlien

+0

我試圖做一個constexpr版本但失敗,constexpr只允許一個return語句。 – StereoMatching

+3

我相當肯定,大多數現代編譯器會自動執行這種優化,就像他們爲'for'循環執行直到一個常量值(例如'for(int i = 0; i <5; i ++)')一樣。你必須檢查確定。 – ShdNx

回答

20

C++中的模板元編程是純粹的函數式編程,而在純函數式編程中,您不會像使用或使用循環一樣使用循環,而且根本沒有任何可變數據。你所有的只是遞歸。爲了使遞歸工作更容易,你需要提高抽象層次。您有遞歸的代碼是好的,但迭代和工作可以分道揚鑣:

template <int First, int Last> 
struct static_for 
{ 
    template <typename Fn> 
    void operator()(Fn const& fn) const 
    { 
     if (First < Last) 
     { 
      fn(First); 
      static_for<First+1, Last>()(fn); 
     } 
    } 
}; 

template <int N> 
struct static_for<N, N> 
{ 
    template <typename Fn> 
    void operator()(Fn const& fn) const 
    { } 
}; 

既然你有這樣的元函數,你可以寫你的deduce_mask功能是這樣的:

template<int Channel> 
void deduce_mask(Matrix const &src, int mask[]) 
{ 
    static_for<0, Channel>()([&](int i) 
    {    
     mask[mapper(0, 1, i)] = src(row - 1, col)[i]; 
     mask[mapper(1, 1, i)] = src(row, col)[i]; 
     mask[mapper(2, 1, i)] = src(row + 1, col)[i];  
    }); 
} 

的Visual C++ 2012與/ Ob1進行命令行開關編譯此代碼到該:

push  0 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  1 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  2 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  3 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  4 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
... 

如果無法使用lambda函數,需要編寫一個仿函數。 Functor與lambda函數相比有一個優勢 - 你可以指定一個調用約定(如果你不介意的話)。如果函子的operator()函數有__fastcall調用約定,那麼在彙編代碼中將看到mov edx, x而不是push x

+0

謝謝,這個答案是非常優雅的(至少對我來說) – StereoMatching

+5

但是,並不是所有這些'調用'比正常循環慢嗎?爲什麼編譯器不能優化它們? – Kapichu

2

如果您希望將索引放入模板中,lego的響應雖然優雅,但非常棒,無法編譯。 std::get<i>(some_tuple)

如果要實現在未來的這個附加功能,下面的代碼將工作,並應與樂高的解決方案向後兼容(除了我用一個靜態的應用,而不是運營商()方法):

template <int First, int Last> 
struct static_for 
{ 
    template <typename Lambda> 
    static inline constexpr void apply(Lambda const& f) 
    { 
     if (First < Last) 
     { 
      f(std::integral_constant<int, First>{}); 
      static_for<First + 1, Last>::apply(f); 
     } 
    } 
}; 
template <int N> 
struct static_for<N, N> 
{ 
    template <typename Lambda> 
    static inline constexpr void apply(Lambda const& f) {} 
}; 

現在,你可以做到以下幾點:

static_for<0, Channel>::apply([&](auto i) // Changed from '(int i)'. In general, 'auto' will be a better choice for metaprogramming! 
{    
    // code... 
    mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change 
    std::get<i.value>(some_tuple); // But here you must get the member .value 
    // more code... 
}); 

在VC++測試2015年我沒有研究爲什麼這個工作,但我只能假設,std::integral_constant<T,...>定義隱式轉換爲T使用VA但是編譯器無法確定隱式強制轉換會生成constexpr,因此您必須使用i.value(它是constexpr)來檢索該值。

尋址@湯姆的問題在評論 如果你想遍歷一個參數包,你可以做以下的(同一實現):

template<typename... Args> 
inline constexpr auto foo(const Args&... args) 
{ 
    static_for<0,sizeof...(Args)>::apply([&](auto N) 
    { 
     std::cout << std::get<N.value>(std::make_tuple(args...)); 
    }); 
} 

foo(1,"a",2.5); // This does exactly what you think it would do 

如果std::get<N.value>(std::make_tuple(args...))長相醜陋,你可以創建另一個constexpr函數可以最小化代碼。

+0

我認爲你需要將'(int i)'改成'(auto i)',因爲int :: value不好形成 – Caleth

+0

@Caleth好的!從多個來源複製/粘貼的結果。 – AOK

+0

令人驚歎!這使我的代碼更具可讀性。 – tom

2

With if constexpr我們可以改進AOK的解決方案。

template <int First, int Last, typename Lambda> 
inline void static_for(Lambda const& f) 
{ 
    if constexpr (First < Last) 
     { 
     f(std::integral_constant<int, First>{}); 
     static_for<First + 1, Last>(f); 
     } 
} 

有了這一點,我們可以擺脫那個::apply

static_for<0, Channel>([&](auto i) 
{    
    // code... 
    mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change 
    std::get<i.value>(some_tuple); // But here you must get the member .value 
    // more code... 
}); 

不幸的是你還是得寫i.value


請注意,這將不無if constexpr是可能的,因爲AOK的方式將需要static_for局部模板特殊化。

相關問題