次の例を使用して、質問を説明します。
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;
他の解決策はありますか?
std::is_signed
はstd::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>::value
はtrue
であり、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;
}
いかがですか static assert
std::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
私は使うだろう 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
への呼び出しの値で、まさにそれがあなたが探しているものです。
別のオプションとして、おそらくstatic_assertとstd :: 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
その結果、プログラムは何を期待していますか?現状では、差分の結果として符号なしを返します。私見、これは発生するのを待っているバグです。
#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
を作成するためにキャストに頼らなければならない場合があり、そうでない場合、数学は機能しません。
したがって、私にはあなたの機能に関していくつかの問題があります。
まず、関数では、左、右、結果の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
を呼び出せるかどうかを確認する他のコードはサポートしません。それは単にハードエラーを生成します。