2012-12-11 7 views
15

このようなものが存在する可能性はありますか?C++で静的forループを開発することは可能ですか?

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は1つのreturn文しか許可しません。 – StereoMatching

+3

ほとんどの現代のコンパイラは、 'for(int i = 0; i <5; i ++)')などの定数値まで、 'for'ループの場合と同じように、この最適化を自動的に行います。あなたは確かに確かめるためにチェックする必要があります。 – ShdNx

答えて

20

C++でのテンプレートメタプログラミングは、純粋な関数型プログラミングであり、純粋な関数型プログラミングでは、forやwhileのようなループを使用することはなく、変更可能なデータはまったくありません。あなたが持っているのは再帰だけです。再帰を簡単に処理するには、抽象レベルを少し上げる必要があります。あなたが持っている再帰的なコードは罰金ですが、反復作業を離れて分割することができます:

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];  
    }); 
} 
/Ob1のコマンドラインスイッチと

のVisual C++ 2012は、このにこのコードをコンパイルします。

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) 
... 

あなたはラムダ関数を使用できない場合、あなたはファンクタを記述する必要があります。 Functorはlambda関数に比べて1つの利点があります - 呼び出し規約を指定することができます(もしそうしても構いません)。ファンクタの演算子()に__fastcall呼び出し規約がある場合、アセンブラコードにpush xの代わりにmov edx, xが表示されます。

+0

ありがとう、この答えはかなり優雅です(少なくとも私のために) – StereoMatching

+5

しかし、それらの 'call'は通常のforループを持つよりも遅くはありませんか?コンパイラがそれらを最適化しないのはなぜですか? – Kapichu

2

エレガントで素晴らしいとはいえ、インデックスをテンプレートに入れる場合は、レゴのレスポンスはコンパイルされません。 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) {} 
}; 

今、あなたは、次の操作を行うことができます

VC++ 2015でテスト
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... 
}); 

この作品、なぜ私が研究していなかったが、私は唯一のstd::integral_constant<T,...>がVAを使用してTへの暗黙的なキャストを定義すると仮定することができますコンパイラは、暗黙的なキャストがconstexprを生成することを理解できないため、i.valueconstexpr)を使用して値を取得する必要があります。コメント でトムの質問@アドレッシング

あなたは、パラメータパックを反復処理したい場合は、以下の(同じ実装)を行うことができます。

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 :: value'が悪いので、'(int i) 'を'(auto i) 'に変更する必要があると思います。 – Caleth

+0

@Caleth Good catch!複数のソースからコピー/貼り付けした結果。 – AOK

+0

すごい!これは私のコードをはるかに読みやすくします。 – tom

2

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); 
     } 
} 

私たちは、残念ながら、あなたはまだi.valueを記述する必要があり、その::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... 
}); 

を取り除くことができます。 AOKの方法はstatic_forのテンプレートの部分特殊化を必要とするので、これはif constexprなしで可能ではないことを


は注意してください。

関連する問題