web-dev-qa-db-ja.com

テンプレートパラメータタイプを強制的に署名するにはどうすればよいですか?

次の例を使用して、質問を説明します。

template<typename T>
T diff(T a, T b)
{
  return a-b;
}

このテンプレート関数は、T型が署名されている場合にのみ機能すると思います。私が理解できる唯一の解決策は、すべての署名されていない型にdeleteキーワードを使用することです。

template<>
unsigned char diff(unsigned char,unsigned char) == delete;
template<>
unsigned char diff(unsigned char,unsigned char) == delete;

他の解決策はありますか?

31
feelfree

std::is_signedstd::enable_ifと一緒に使用できます。

template<typename T>
T diff(T a, T b);

template<typename T>
std::enable_if_t<std::is_signed<T>::value, T> diff(T a, T b) {
    return a - b;
}

ここでstd::is_signed<T>::valuetrueであり、Tが署名されている場合に限ります(浮動小数点型のtrueも必要ない場合は、std::is_integralと組み合わせることを検討してください)。

std::enable_if_t<Test, Type>std::enable_if<Test, Type>::typeと同じです。 std::enable_if<Test, Type>は、Testがfalseの場合は空の構造体として定義され、それ以外の場合はtypedef typeのみがテンプレートパラメータTypeと等しい構造体として定義されます。

したがって、符号付きの型の場合、std::enable_if_t<std::is_signed<T>::value, T>Tと同じですが、未署名の場合は定義されず、コンパイラはSFINAEルールを使用するため、特定の非署名の型の実装を指定する必要がある場合は、簡単に行うことができます:

template<>
unsigned diff(unsigned, unsigned)
{
    return 0u;
}

いくつかの関連リンク: enable_ifis_signed

45
alexeykuzmin0

いかがですか static assertstd::is_signed

template<typename T>
T diff(T a, T b)
{
    static_assert(std::is_signed<T>::value, "signed values only");
    return a-b;
}

ライブでご覧ください: http://ideone.com/l8nWYQ

35
Louen

私は使うだろう static_assertニースのエラーメッセージが表示されます。 enable_ifは、問題があるIDEのみを取得し、次のようなメッセージでコンパイルに失敗します

識別子diffが見つかりません

これはあまり役に立ちません。

だから、なぜこのようにしないでください:

#include <type_traits>

template <typename T>
T diff(T a, T b)
{
    static_assert(std::is_signed< T >::value, "T should be signed");
    return a - b;
}

こうすることで、署名された型以外のものを使用してdiffを呼び出すと、コンパイラーにこの種のメッセージが書き込まれます。

エラー:Tに署名する必要があります

場所とdiffへの呼び出しの値で、まさにそれがあなたが探しているものです。

11
mister why

別のオプションとして、おそらくstatic_assertstd :: is_signed type traitを追加します:

template<typename T>
auto diff(T x, T y)
{
    static_assert(std::is_signed<T>::value, "Does not work for unsigned");
    return x - y;
}

そのため:

auto x = diff(4, 2); // works
auto x = diff(4U, 2U); // does not work
7
Edgar Rokjān

その結果、プログラムは何を期待していますか?現状では、差分の結果として符号なしを返します。私見、これは発生するのを待っているバグです。

#include <type_trait>

template<typename T>
auto diff(T&& a, T&& b)
{
    static_assert (std::is_unsigned<T>::value);
    return typename std::make_signed<T>::type(a - b);
}

これを書くためのより現代的な待機:

inline auto diff(const auto a, const auto b)
{
    static_assert (   std::is_unsigned<decltype(a)>::value 
                   && std::is_unsigned<decltype(b)>::value );
    return typename std::make_signed<decltype(a -b)>::type(a - b);
}

[編集]私はこのコメントを追加する必要性を感じています。数学の方程式で符号なし整数型を使用することは常にトリッキーです。上記の例は、あらゆる数学パッケージの非常に便利なアドオンです。実際の状況では、差分の結果signedを作成するためにキャストに頼らなければならない場合があり、そうでない場合、数学は機能しません。

2
Michaël Roy

したがって、私にはあなたの機能に関していくつかの問題があります。

まず、関数では、左、右、結果の3つのタイプすべてが一致する必要があります。したがって、signed char a; int b; diff(a-b);は理由もなく機能しません。

template<class L, class R>
auto diff( L l, R r )
-> typename std::enable_if<
  std::is_signed<L>::value && std::is_signed<R>::value,
  typename std::decay<decltype( l-r )>::type
>::type
{
  return l-r;
}

2番目にしたいのは、diffオブジェクトを作成することです。 diff関数を簡単に渡すことはできず、高次関数は素晴らしいです。

struct diff_t {
  template<class L, class R>
  auto operator()(L l, R r)const
  -> decltype( diff(l,r) )
  { return diff(l,r); }
};

これで、diff_t{}をアルゴリズムに渡すことができます。これは、アルゴリズムがdiffの「オーバーロードセット」を1つの(自明な)C++オブジェクトに保持しているためです。

これは深刻な過剰です。単純なstatic_assertも機能します。

static_assertはより適切なエラーメッセージを生成しますが、SFINAEを使用してdiffを呼び出せるかどうかを確認する他のコードはサポートしません。それは単にハードエラーを生成します。