19

このようなものが存在する可能性はありますか?

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[])
    {

    }
};

2番目の解決策は、コンパイラーにコンパイル時に結果を把握させたいときに思いつくことができる唯一の解決策です。メタプログラミングソリューションのように、「i」を定数値にする簡単な方法はありますか?私にとって、単純なforループは、メタプログラミングバージョンよりもはるかに簡単に操作できます。

4

4 に答える 4

23

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コマンドラインスイッチを備えたVisualC++ 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には、ラムダ関数に比べて1つの利点があります。呼び出し規約を指定できます(それを実行してもかまわない場合)。ファンクターのoperator()に__fastcall呼び出し規約がある場合は、アセンブラーコードのmov edx, x代わりに表示されます。push x

于 2013-01-09T23:39:41.930 に答える
10

これにより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 constexprAOKの方法では、の部分的なテンプレートの特殊化が必要になるため、これがないと不可能であることに注意してくださいstatic_for

于 2017-08-30T19:41:06.070 に答える
5

レゴの応答は、エレガントで素晴らしいものですが、インデックスをテンプレートに入れたい場合はコンパイルされません-例:std::get<i>(some_tuple)

将来この追加機能を実装したい場合は、以下のコードが機能し、レゴのソリューションと下位互換性があるはずです(operator()の代わりに静的applyメソッドを使用することを除いて):

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

std::integral_constant<T,...>VC ++ 2015でテストされました。これが機能する理由は調査していませんが、値を使用するための暗黙のキャストを定義していると推測できTますが、コンパイラは暗黙のキャストがを生成することを理解できないconstexprため、値を取得する必要がありますを使用します。i.valueこれはconstexprです。

コメント内の@tomの質問への対処 パラメーターパックを反復処理する場合は、次のようにすることができます(同じ実装)。

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、コードを最小化する別の関数を作成できます。

于 2017-08-22T13:30:31.180 に答える
0

を使用して、コンパイル時にタプルをループできるはずですfor...。私の理解では、次のように構造体メンバーをループすることさえできるはずです。

#include <iostream>
#include <string>
#include <tuple>
#include <utility>

struct Agg { int a;
             std::string b;
             double c;};

template <typename... T>
void print (std::tuple<T...> const & t) {
        for... (auto const & member: t)
                std::cout << member << '\n';
}

int main () {
        auto agg = Agg{1,"bla",2.1};    
        print (agg);
}

TimurDoumlerのこのvideo@47:44を参照してください。トランク(またはマスター)のgcc(またはclang)でさえ、このc ++ 20の種類のコンパイル時ループをまだサポートしていないことに注意してください-したがって、私はテストしませんでした。for...ところで、句読点を使って検索できる検索エンジンを知っている人はいますか?

于 2019-07-22T20:45:18.160 に答える