7

異なる構造体の (数値) フィールドをインクリメントする単一のテンプレート関数を作成することは可能ですか? 例えば:

struct Color
{
    ubyte a,r,g,b;
}

struct Point
{
    double x, y;
}

私はこのようなことを試しました:

T update(T, A)(T t, A a)
if (is(T == struct))
{
    auto vals = t.tupleof;
    foreach (i; 0 .. vals.length) {
        vals[i] += a; // error: i cannot be read at compile time
    }
    return T(vals); // convert back to struct
}

タプルを受け入れる関数テンプレートも作成しようとしましたが、タプルは常に展開されているため、コンパイラが正しいテンプレートに一致しません。ありがとう。

4

1 に答える 1

12

ええと、あなたがやろうとしていることはかなり奇妙だと思いますが、それは確かに可能です。最も素朴なインプレースの方法は、おそらく次のようになります。

void update(T)(ref T t)
    if(is(T == struct))
{
    foreach(ref var; t.tupleof)
        ++var;
}

コピーを使用してそれを行う最も簡単な方法は、おそらく、更新された値で新しいものを構築しようとするのではなく、コピーしてから更新することです(ただし、本当に必要な場合は、それも実行できると確信しています)。

T update(T)(T t)
    if(is(T == struct))
{
    auto copy = t;

    foreach(ref var; copy.tupleof)
        ++var;

    return copy;
}

もちろん、ここでの主な問題は、これらの両方に対するテンプレートの制約が非常に弱いことです。あなたがしなければならないのはあなたの構造体にインクリメントできないタイプを持っていることだけであり、それは機能しません。これを修正する最も簡単な方法は、おそらくそれをテストするための同名のテンプレートを作成することです。

T update(T)(T t)
    if(isIncrementableStruct!T)
{
    auto copy = t;

    foreach(ref var; copy.tupleof)
        ++var;

    return copy;
}

template isIncrementableStruct(T)
{
    enum isIncrementableStruct = is(T == struct) &&
                                 is(typeof({T t; foreach(var; t.tupleof) ++var;}));
}

また、インクリメント可能なすべてのフィールドをインクリメントして、他のフィールドはそのままにしておきたい場合は、おそらく次のようにします。

T update(T)(T t)
    if(is(T == struct))
{
    auto copy = t;

    foreach(ref var; copy.tupleof)
    {
        static if(canIncrement!(typeof(var)))
            ++var;
    }

    return copy;
}

template canIncrement(T)
{
    enum canIncrement = is(typeof({T var; ++var;}));
}

いずれにせよ、あなたが見逃しているように見える主なことは、要素のコピーを更新するのではなく、要素を更新するために、tupleof使用中に直接反復を試みることでした。ref

于 2012-06-09T06:36:24.847 に答える