7

整数として機能する型を定義しました。タイプの 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() {
}

「部分的な特殊化」は実際には単なる「任意の引数」であり、これは私たちが持っているものよりも特殊化されていません。

したがって、唯一の解決策は、ユーザーに次のいずれかを実行するように要求することです。

  1. common_type の最初の引数として常に bounded_integer を入れます
  2. 常に make_bounded(built-in integer value) 関数を使用して整数を bounded_integer に変換します (したがって、bounded_integer と組み合わせて組み込み型の common_type を特殊化しないでください)。
  3. 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 をintandunsignedに適用します。これは、標準の整数昇格規則に従い、 を与え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_typeintunsignedです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 で次のように定義されています。

それ以外の場合、符号なし整数型を持つオペランドのランクが他のオペランドの型のランク以上である場合、符号付き整数型を持つオペランドは、符号なし整数型を持つオペランドの型に変換されます。

4

4 に答える 4

3

std::common_type独自の 2 引数の特殊化を n 引数のケースに外挿します。引数が 2 つの場合のみ特殊化する必要があります。

template< typename other, int low, int high >
struct common_type< other, ::my::ranged_integer< low, high > > {
    using type = other;
};

template< typename other, int low, int high >
struct common_type< ::my::ranged_integer< low, high >, other > {
    using type = other;
};

template< int low, int high >
struct common_type< ::my::ranged_integer< low, high >,
                    ::my::ranged_integer< low, high > > {
    using type = ::my::ranged_integer< low, high >;
};

common_typeこれにより、異なる範囲の整数の間が未定義のままになります。minと でできると思いますmax

is_ranged_integerクラスが継承をサポートしている場合、トレイトを作成することもできます。

ライブラリを名前空間内に配置することを忘れないでください。

于 2013-09-08T00:42:52.737 に答える
1

可能な実装は次のとおりです。

#include <limits>  
#include <utility>
#include <iostream>

template<typename T, typename U>
static constexpr auto min(T x, U y) -> decltype(x < y ? x : y)
{
    return x < y ? x : y;
}

template<typename T, typename U>
static constexpr auto max(T x, U y) -> decltype(x < y ? x : y)
{
    return x > y ? x : y;
}

template<intmax_t f, intmax_t l>
struct ranged_integer
{
    static intmax_t const first = f;
    static intmax_t const last  = l;
    static_assert(l > f, "invalid range");
};

template <class ...T> struct common_type
{
};

template <class T>
struct common_type<T>
{
    typedef T type;
};

template <class T, class U>
struct common_type<T, U>
{
    typedef decltype(true ? std::declval<T>() : std::declval<U>()) type;
};

template <class T, intmax_t f, intmax_t l>
struct common_type<T, ranged_integer<f,l>>
{
    typedef ranged_integer< min(std::numeric_limits<T>::min(),f) , max(std::numeric_limits<T>::max(),l) > type;
};

template <class T, intmax_t f, intmax_t l>
struct common_type<ranged_integer<f,l>, T>
{
    typedef typename common_type<T, ranged_integer<f,l>>::type type;
};

template <intmax_t f1, intmax_t l1, intmax_t f2, intmax_t l2>
struct common_type<ranged_integer<f1,l1>, ranged_integer<f2,l2>>
{
    typedef ranged_integer< min(f1,f2) , max(l1,l2) > type;
};

template <class T, class U, class... V>
struct common_type<T, U, V...>
{
    typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
};

int main(int argc, char *argv[])
{
    typedef common_type<char, ranged_integer<-99999999, 20>, short, ranged_integer<10, 999999999>, char>::type type;
    std::cout << type::first << std::endl; // -99999999
    std::cout << type::last << std::endl;  // 999999999
    return 0;
}
于 2013-09-08T01:05:04.543 に答える
0

多分私は何かが欠けているかもしれませんが、あなたはこのようなものが欲しいだけではありませんint:

namespace std {

// first give ranged_integer a ground zero
    template<intmax_t minimum, intmax_t maximum>
    class common_type<ranged_integer<minimum, maximum>> {
        typedef typename ranged_integer<minimum, maximum> type;
    };

// sort out int
    template<intmax_t minimum, intmax_t maximum>
    class common_type<int, ranged_integer<minimum, maximum>> {
        typedef typename ranged_integer<minimum, maximum> type;
    };

    template<intmax_t minimum, intmax_t maximum>
    class common_type<ranged_integer<minimum, maximum>, int>> {
        typedef typename ranged_integer<minimum, maximum> type;
    };

`longlong longなどについてこれを繰り返します...そしてのテンプレート定義は、1つのタイプcommon_typeのみのすべての可変個のケースを処理しますか?ranged_integer

于 2013-09-08T00:57:20.900 に答える