6

現在、テンプレート パラメーター A と B に応じて、値を左または右にシフトするテンプレート関数があります。

template <int A, int B> void f(X) {
// ...
if (A >= B)
{
  SetValue(X << (A-B));
}
else // (A < B)
{
  SetValue(X >> (B-A));
}

のテンプレートをインスタンス化するとA<B、(到達不能な) 最初の分岐で右に負のシフトがあるという警告が表示されます。それ以外の場合は、最初の分岐で左に負のシフトがあるという警告が表示されます。私たちのコードベースは警告がないため、これは受け入れられません。これら 2 つのシフト ステートメントに代わる、簡潔で読みやすい代替手段はありますか?

同様の質問 (例: Dynamically shift left OR right ) には、シフト距離がランタイム変数であるため、この誤った警告はありません。

4

12 に答える 12

6

C++11 またはブーストで。

template<int A, int B>
void f_impl(typename std::enable_if<(A >= B)>::type* = 0)
{
   // first case
}

template<int A, int B>
void f_impl(typename std::enable_if<(A < B)>::type* = 0)
{
   // second case
}

template<int A, int B>
void f()
{
   f_impl<A, B>();
}
于 2013-01-31T14:17:56.800 に答える
4

(AB) と (BA) の結果を unsigned にキャストし、さらに でマスク (bitwise-and) し(sizeof(int) - 1)ます。これにより、GCC 5.5 および 6.3 の警告がクリアされます。GCC の最近のバージョンでは、警告は生成されません。

template <int A, int B> void f(int X) {
  // ...
  if (A >= B)
  {
    SetValue(X << ((unsigned)(A-B) & (sizeof(int) - 1)));
  }
  else // (A < B)
  {
    SetValue(X >> ((unsigned)(B-A) & (sizeof(int) - 1)));
  }
}

未定義の動作に関するさまざまなコメントに対処するために注意 してください。この提案されたソリューションが未定義の動作を引き起こす可能性がある唯一の意味は、オペランドのビット幅よりも大きな量のシフトを実行することです。ただし、これは比較によって保護されます。A と B の差が安全なシフト カウントであると仮定すると、これは問題で暗示されているため、if (A >= B)その量のシフトのみが実際に実行されることが保証されます。ifステートメントのもう一方のブランチは実行されないため、シフトを実行せず、シフトから未定義の動作を生成することはできません (ただし、実行された場合は確実に実行されます)。

何人かのコメンターが、実行されていないブランチが依然として未定義の動作を引き起こす可能性があると主張しています。このような誤解がどのように発生するのかについて、私はやや途方に暮れています。次のコードを検討してください。

int *a = nullptr;

if (a != nullptr) {
    *a = 4;
}

これで、null ポインターの逆参照が実行されなくても未定義の動作を引き起こす場合、ガード条件は役に立たなくなります。これは明らかにそうではありません。上記のコードはまったく問題ありません。aの値を割り当て、ガードのためにnullptr逆参照しません。aこのような明白な例 (null への代入の直後に null のチェックが続く) は実際のコードでは発生しない傾向がありますが、一般に「保護された逆参照」は一般的なイディオムです。実際にチェックされたポインターが null の場合、それ自体が未定義の動作を生成することはありません。それがガードが役立つ理由です。

于 2013-01-31T14:17:48.213 に答える
3

最も明白なのは、追加の引数を取る関数に転送することです:

template <bool Cond> struct Discrim {};

template <int A, int B>
void f( Discrim<false> )
{
    SetValue( X, (A - B) );
}

template <int A, int B>
void f( Discrim<true> )
{
    SetValue( X, (B - A) );
}

template <int A, int B>
void f()
{
    f( Discrim< (A < B) >() );
}

(このような Discrim クラス テンプレートの使用は、より単純なメタプログラミング手法の 1 つです。)

于 2013-01-31T14:20:38.317 に答える
1

davmac のコメント ("use &0x1F") は、想定される最大シフト幅を除いて、正しい考えでした。それは簡単に修正されました:

template <int A, int B> void f(X) {
// ...
if (A >= B)
{
  SetValue(X << abs(A-B));
}
else // (A < B)
{
  SetValue(X >> abs(B-A));
}
于 2013-01-31T14:45:13.860 に答える
0
template< int A, int B > void f(X)
{
    std::function< int(int, int) > shift =
        A < B
        ? [](int X, int N) { return X << N; }
        : [](int X, int N) { return X >> N; }

    SetValue( shift( X, std::max(A,B) - std::min(A,B) ) );
}
于 2013-01-31T15:13:42.393 に答える
0

シフト操作を別の構造体に入れてstd::conditional、C++11で使用できます。

template <typename A, typename B, typename X>
struct ShiftRight
{
    static void shift() { SetValue(X >> (A - B)); }
};

template <typename A, typename B, typename X>
struct ShiftLeft
{
    static void shift() { SetValue(X << (A - B)); }
};

template <typename A, typename B, typename X>
void f()
{
    typedef typename std::conditional<A >= B, ShiftLeft<A, B, X>, ShiftRight<A, B, X>>::type ShiftType;

    ShiftType::shift();
}
于 2013-01-31T14:20:54.000 に答える
0

新しいテンプレートを追加して、適切に特殊化できます。たとえば、次のようになります。

template<bool b> int Shift(int i, int a, int b);

template<true> int Shift(int i, int a, int b) { return i << (a-b); }
template<false> int Shift(int i, int a, int b) { return i >> (b-a); }

そして、それを として呼び出しますShift<(A >= B)>(X, A, B)。それはうまくいくはずです。

于 2013-01-31T14:19:04.840 に答える
0

このようなものはどうですか:-

#include <iostream>

template <int A, int B, bool D> class shift
{
};

template <int A, int B> class shift<A, B, false>
{
public:
    static int result(int x) {return x << (B-A);}
};

template <int A, int B> class shift<A, B, true>
{
public:
    static int result(int x) {return x >> (A-B);}
};


template <int A, int B> int f(int x)
{
    return shift<A, B, (A>B)>::result(x);
}

int main()
{
    std::cout << f<1, 2>(10) << "\n";
    std::cout << f<2, 1>(10) << "\n";
}
于 2013-01-31T15:54:06.987 に答える
0

私の頭の上から:

template <int A, int B> struct whatever {
    static void f() {
        SetValue(X << (A - B));
    }
};

template <int A, int B, bool reversed> helper : whatever<A, B> {
};

template <int A, int B, true> : helper whatever<B, A> {
};

template <int A, int B> do_it : helper<A, B, B < A> {
};

template <int A, int B> void f() {
    return do_it<A, B>::f();
}
于 2013-01-31T14:19:53.117 に答える