对于出现在可变参数模板参数包的任何位置的类型的类模板的部分特化

Dav*_*one 7 c++ template-specialization template-meta-programming variadic-templates c++11

我已经定义了一个充当整数的类型.我想为我的类型定义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
Run Code Online (Sandbox Code Playgroud)

我第一次尝试解决这个问题是使用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() {
}
Run Code Online (Sandbox Code Playgroud)

"部分专业化"实际上只是"任何论据",而不是我们所拥有的专业.

因此,似乎唯一的解决方案是要求我的用户执行以下操作之一:

  1. 总是将bounded_integer作为common_type的第一个参数
  2. 总是使用我的make_bounded(内置整数值)函数将它们的整数转换为bounded_integer(因此没有内置类型的common_type的特殊化与bounded_integer的组合)
  3. 从来没有把bounded_integer放在大于N的位置,其中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.
Run Code Online (Sandbox Code Playgroud)

对于我无法更改原始定义的类,是否有更好的方法来实现此目的(当所有类型满足一个条件并且任何类型满足另一个条件时,模板专门化)?

编辑:

根据答案,我的问题不够明确.

首先,预期的行为:

如果有人调用std :: common_type,其中所有类型都是bounded_integer或内置数字类型的实例,我希望结果是bounded_integer,它具有所有可能的最小值和最大值的最小值.可能的最大值.

问题:

当有人在任意数量的bounded_integer上调用std :: common_type时,我有一个有效的解决方案.但是,如果我只专门化两个参数版本,那么我遇到了以下问题:

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和unsigned,遵循标准的整数提升规则,给出unsigned.然后,它返回的结果common_type与unsigned我的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.Standardese可以在§20.9.7.6/3中找到,其中common_type两个值是

typedef decltype(true ? declval<T>() : declval<U>()) type;

在第5.16/6节中,它说

第二和第三个操作数具有算术或枚举类型; 执行通常的算术转换以使它们成为公共类型,结果是该类型.

通常的算术转换在§5/ 9中定义为

否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则具有有符号整数类型的操作数应转换为具有无符号整数类型的操作数的类型.

Pot*_*ter 4

std::common_type将其自己的双参数专业化外推到 n 参数情况。您只需要专门化两个参数的情况。

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 >;
};
Run Code Online (Sandbox Code Playgroud)

这使得common_type不同范围的整数之间的值未定义。min我想你可以用和 来做到这一点max。

is_ranged_integer如果您的类支持继承,您也可以创建一个特征。

不要忘记将您的库放入命名空间中。