templateの特殊化(error C2768 の解消方法)

[戻る]
ホウジョウウサギ 2011/09/13(火) 22:24:24
環境:WinXP, VS2005

やりたいことは,
「2つの定数値 S1,S2 の差」bit分だけint型の値valを(左)シフトしたい…具体的には
 result = val << ( S1 - S2 );
のような感じです.
ここで,(S1-S2)<0 の場合には右シフトとなってほしいのですが,
シフト演算子の右辺値に負の値を用いることはできません.

S1,S2は様々な値を取る(複数の {S1,S2} 定数値ペアが存在する)ものの,
コンパイル時に定まるものなので,templateで対処できそうだと考え,
以下のようなコードを書きました.

S1,S2のほかにbool型のtemplate引数を用意し,
S1とS2の大小関係でtrueとfalseに特殊化しようとしたのですが,
error C2768 というエラーが発生してしまい困っています.

このエラーを解消するためにはどのように記述を改めれば良いのでしょうか?
よろしくおねがいします.


---以下,エラーの出るコード---


template < int S1, int S2, bool S1_GreEq >
int Shift_( int val )
{    return shift;    }    //ダミー(この実装はなくてもいい?)

template < int S1, int S2 >
int Shift_< S1, S2, true >( int val )
{    return ( val << ( S1 - S2 ) );    }    //error C2768 : 明示的なテンプレート
引数を使用することはできません。

template < int S1, int S2 >
int Shift_< S1, S2, false >( int val )
{    return ( val >> ( S2 - S1 ) );    }

//特殊化された関数のどちらかを使って目的を達成する
template < int S1, int S2 >
int Shift( int val )
{    return Shift_< S1, S2, (S1>=S2) >( val );    }

ホウジョウウサギ 2011/09/13(火) 22:30:50
記述誤りがありました.

×
>{    return shift;    }    //ダミー(この実装はなくてもいい?)

↓

{   return val;   }

です.

仲澤@失業者 2011/09/14(水) 10:31:51
template< typename T> T SSS( T x, T s1, T s2){
    T sft = s1 - s2;
    if( sft > 0)  return x << sft;
    return      x >>( -sft);
}
const int    x0    = SSS<int>( 8, 4, 5);
const int    x1    = SSS<int>( 9, 5, 4);
ぢゃあ、だめでしょうか。
コンパイルしてません。あしからず。

ホウジョウウサギ 2011/09/14(水) 11:11:51
仲澤@失業者様,代案ありがとうございます.
(とりあえず型はintと決まっているので int SSS( int, int, int )でOKです)

提示していただいたものは

template < int S1, int S2 >
int Shift( int val )
{    return ( S1>=S2  ?  val<<(S1-S2)  :  val>>(S2-S1) );    }

と同等(実行時にS1とS2の大小関係を見て分岐する点で)かと思います.
現在,当方でも↑としてお茶を濁しており,
動作としてはこれで十分なのですが,
この条件分岐をコンパイル時にやってしまえないかなぁと思い,
怪しげなtemplate記述に挑戦している次第なのですが,そういったことは無理でしょう
か?

あと,コンパイル時に
「warning C4293 : シフト数が負の数であるか,大きすぎます.定義されていない動作で
す」
が << と >> に2つ出てしまうのもちょっと嫌な感じです.

yoh2 2011/09/14(水) 13:11:25
関数テンプレートの部分特殊化が許されていないのがエラーの原因なので、
Shiftを関数ではなくファンクタにしてみてはいかが?

以下実装例。VS2005では未確認。

template<int S1, int S2, bool S1_GreEq = (S1 >= S2) >
struct Shift;

template<int S1, int S2>
struct Shift<S1, S2, true>
{
  int operator()(int val) const { return val << (S1 - S2); }
};

template<int S1, int S2>
struct Shift<S1, S2, false>
{
  int operator()(int val) const { return val >> (S2 - S1); }
};

// 使用例
int main()
{
  std::cout << Shift<3, 1>()(16) << std::endl; // 16 << (3 - 1) = 64
  std::cout << Shift<2, 3>()(16) << std::endl; // 16 >> (3 - 2) = 8
  std::cout << Shift<2, 2>()(16) << std::endl; // 16 << (2 - 2) = 16
  return 0;
}

ホウジョウウサギ 2011/09/14(水) 15:11:51
[[解決]]
yoh2様の方法でいけることを確認しました.
ありがとうございました.

ただ,
template<int S1, int S2, bool S1_GreEq = (S1 >= S2) >
とすると,
「warning C4348 : 既定パラメータの再定義です:パラメータ3」
と言われてしまうようなので,デフォルトを与えずに

template < int S1, int S2 >
int DoShift( int val )
{    return Shift< S1, S2, (S1>=S2) >( val );    }

とすることにしました.

古いスレッドには追加発言できません。