整数として機能する型を定義しました。タイプの std::common_type の特殊化を定義したいと考えています。ただし、この特殊化により、bounded_integer (私のクラス) の common_type を、他の bounded_integer または組み込みの整数型の任意の数の他の引数と組み合わせて指定できるはずです。次のコードをすべて有効にしたい:
std::common_type<bounded_integer<1, 10>>::type
std::common_type<bounded_integer<1, 10>, int>::type
std::common_type<int, long, bounded_integer<1, 10>>::type
std::common_type<int, int, long, short, long long, short, bounded_integer<1, 10>, int, short, short, short, ..., short, bounded_integer<1, 10>>::type
この問題を解決する最初の試みは、enable_if を使用することでした。しかし、これでは common_type のライブラリ定義と区別できないことに気付きました。
#include <type_traits>
class C {};
template<typename T, typename... Ts>
class contains_c {
public:
static constexpr bool value = contains_c<T>::value or contains_c<Ts...>::value;
};
template<typename T>
class contains_c<T> {
public:
static constexpr bool value = std::is_same<T, C>::value;
};
namespace std {
template<typename... Args, typename std::enable_if<contains_c<Args...>::value>::type>
class common_type<Args...> {
public:
using type = C;
};
} // namespace std
int main() {
}
「部分的な特殊化」は実際には単なる「任意の引数」であり、これは私たちが持っているものよりも特殊化されていません。
したがって、唯一の解決策は、ユーザーに次のいずれかを実行するように要求することです。
- common_type の最初の引数として常に bounded_integer を入れます
- 常に make_bounded(built-in integer value) 関数を使用して整数を bounded_integer に変換します (したがって、bounded_integer と組み合わせて組み込み型の common_type を特殊化しないでください)。
- N より大きい位置に bounded_integer を配置しないでください。ここで N は、Visual Studio の古い可変個引数テンプレートの回避策と同様に、私が決定した数値です。
3 は次のようになります。
// all_bounded_integer_or_integral and all_are_integral defined elsewhere with obvious definitions
template<intmax_t minimum, intmax_t maximum, typename... Ts, typename = type std::enable_if<all_bounded_integer_or_integral<Ts...>::value>::type>
class common_type<bounded_integer<minimum, maximum>, Ts...> {
};
template<typename T1, intmax_t minimum, intmax_t maximum, typename... Ts, typename = typename std::enable_if<all_are_integral<T1>::value>::type, typename = typename std::enable_if<all_bounded_integer_or_builtin<Ts...>::value>::type>
class common_type<T1, bounded_integer<minimum, maximum>, Ts...> {
};
template<typename T1, typename T2, intmax_t minimum, intmax_t maximum, typename... Ts, typename = typename std::enable_if<all_are_integral<T1, T2>::value>::type, typename = typename std::enable_if<all_bounded_integer_or_builtin<Ts...>::value>::type>
class common_type<T1, T2, bounded_integer<minimum, maximum>, Ts...> {
};
// etc.
元の定義を変更できないクラスに対して、これを達成するためのより良い方法 (すべての型が 1 つの条件を満たし、いずれかの型が別の条件を満たしている場合のテンプレートの特殊化) はありますか?
編集:
答えに基づいて、私の問題は十分に明確ではありませんでした。
まず、予想される動作:
誰かが std::common_type を呼び出して、すべての型が bounded_integer または組み込みの数値型のインスタンスである場合、結果を、可能なすべての最小値の最小値とすべての最大値を持つ bounded_integer にしたいと考えています。可能な最大値。
問題:
誰かが任意の数のbounded_integerでstd::common_typeを呼び出すと、私は実用的な解決策を持っています。ただし、引数が 2 つのバージョンのみを特殊化すると、次の問題が発生します。
std::common_type<int, unsigned, bounded_integer<0, std::numeric_limits<unsigned>::max() + 1>
私に与えるべきです
bounded_integer<std::numeric_limits<int>::min(), std::numeric_limits<unsigned>::max() + 1>
ただし、そうではありません。最初に common_type をint
andunsigned
に適用します。これは、標準の整数昇格規則に従い、 を与えunsigned
ます。common_type
次にwithunsigned
と myの結果bounded_integer
を返します。
bounded_integer<0, std::numeric_limits<unsigned>::max() + 1>
そのため、パラメーター パックの途中に追加unsigned
することで、結果の型にはまったく影響を与えないはずですが (その範囲は他のすべての型の範囲内に完全に含まれています)、それでも結果に影響を与えます。これを防ぐために私が考えることができる唯一の方法はstd::common_type
、任意の数の組み込み整数の後にbounded_integer
、その後に任意の数の組み込み整数またはbounded_integer
.
私の質問は次のとおりです。任意の数のパラメーターに続いてパラメーターパックを手動で書き出すことで、概算する必要なくこれを行うにはどうすればよいですかbounded_integer
、またはこれは不可能ですか?
編集2:
common_type が間違った値を与える理由は、標準に従ったこの推論によって説明できます (N3337 からの引用)。
のcommon_type
とint
はunsigned
ですunsigned
。例: http://ideone.com/9IxKIW。標準化は§ 20.9.7.6/3 に記載されておりcommon_type
、2 つの値の
typedef decltype(true ? declval<T>() : declval<U>()) type;
§ 5.16/6 では、次のように述べています。
2 番目と 3 番目のオペランドは算術型または列挙型です。通常の算術変換が実行されて共通の型になり、結果はその型になります。
通常の算術変換は、§ 5/9 で次のように定義されています。
それ以外の場合、符号なし整数型を持つオペランドのランクが他のオペランドの型のランク以上である場合、符号付き整数型を持つオペランドは、符号なし整数型を持つオペランドの型に変換されます。