[英]Partial template specialization depending on a variadic pack of integral constants of mixed type
[英]Partial specialization of class template for a type that appears in any position of a variadic template parameter pack
我已经定义了一个充当整数的类型。 我想为我的类型定义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() {
}
“部分专业化”实际上只是“任何论据”,而不是我们所拥有的专业。
因此,似乎唯一的解决方案是要求我的用户执行以下操作之一:
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.
对于我无法更改原始定义的类,是否有更好的方法来实现此目的(当所有类型满足一个条件并且任何类型满足另一个条件时,模板专门化)?
编辑:
根据答案,我的问题不够明确。
首先,预期的行为:
如果有人调用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)
int
和unsigned
的common_type
是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中定义为
否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则具有有符号整数类型的操作数应转换为具有无符号整数类型的操作数的类型。
std::common_type
将自己的双参数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 >;
};
这样common_type
定义了不同范围整数之间的common_type
。 我想你可以用min
和max
来做。
如果您的类支持继承,您还可以创建一个is_ranged_integer
特征。
不要忘记将您的库放在命名空间中。
简短的回答
如果绝对需要使用标准库提供的std::common_type
,那么除了您自己观察到的3种替代方法之外,没有比这更好的方法了。 如果用户定义的common_type
被认为是可接受的,那么您可以实现您想要的,如下所示。
答案很长
当你说std::common_type<unsigned [long [long]] int, [long [long]] int>::type
将产生unsigned [long [long]]
。 然而,由于common_ type
涉及到任何表达的ranged_integer
本身是ranged_integer
并考虑到您的涉及特ranged_integer
正确地推断出范围,这里只有一个问题,如果成对common_type
之前的类型的[long [long]] unsigned
率[long [long]] int
。那留给我们只有六个情况下,我们要解决办法,即̶̶ ̶s̶t̶d̶:̶:̶c̶o̶m̶m̶o̶n̶_̶t̶y̶p̶e̶<̶u̶n̶s̶i̶g̶n̶e̶d̶ ̶[̶l̶o̶n̶g̶ ̶[̶l̶o̶n̶g̶]̶]̶ ̶i̶n̶t̶,̶ ̶[̶l̶o̶n̶g̶ ̶[̶l̶o̶n̶g̶]̶]̶ ̶i̶n̶t̶>̶:̶:̶t̶y̶p̶e̶
̶及其排序̶p̶e̶r̶m̶u̶t̶a̶t̶i̶o̶n̶s̶.̶̶(̶I̶'̶m̶忽略固定宽度的类型以下,̶但延伸的 想法,认为他们应该直截了当)̶
̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶
实际上我们不能按照 n3485
[meta.type.synop]第1段
“除非另有说明,否则为template <class... T> common_type
[ template <class... T> common_type
]中定义的任何类模板添加特殊化的程序的行为是未定义的。”
[meta.trans.other]表57
[...]如果特化中的至少一个模板参数是用户定义的类型,则程序可以专门化该特征[ template <class... T> common_type
]。 [...] “
这意味着没有有效的方法来覆盖std::common_type<unsigned [long [long]] int, [long [long]] int>::type
,标准要求它总是产生unsigned [long [long]] int
如前所述。
替代std::common_type
当应用于基本整数类型时,克服std::common_type
限制的另一种方法是定义自定义common_type
。
假设ranged_integer
定义如下。
template<typename T, T min, T max>
struct basic_ranged_integer;
template<std::intmax_t min, std::intmax_t max>
using ranged_integer = basic_ranged_integer<std::intmax_t, min, max>;
自定义common_type
可以定义如下。
首先是左递归:
template<typename... T>
struct common_type;
template<typename T, typename U, typename... V>
struct common_type<T, U, V...> :
common_type<typename common_type<T, U>::type, V...> //left recursion
{};
现在涉及basic_ranged_integer
的专业化。
//two basic_ranged_integer
template<typename T, T minT, T maxT, typename U, U minU, U maxU>
struct common_type<basic_ranged_integer<T, minT, maxT>, basic_ranged_integer<U, minU, maxU>>
{
//gory details go here
};
//basic_ranged_integer mixed with primitive integer types
//forwards to the case involving two basic_ranged_integer
template<typename T, T minT, T maxT, typename U>
struct common_type<basic_ranged_integer<T, minT, maxT>, U> :
common_type
<
basic_ranged_integer<T, minT, maxT>,
typename make_ranged_integer<U>::type
>
{};
template<typename T, typename U, U minU, U maxU>
struct common_type<T, basic_ranged_integer<U, minU, maxU>> :
common_type
<
typename make_ranged_integer<T>::type,
basic_ranged_integer<U, minU, maxU>
>
{};
最后是涉及有符号和无符号原始整数组合的特化。
//base case: forwards to the satandard library
template<typename T>
struct common_type<T> :
std::common_type<T>
{};
template<typename T, typename U>
struct common_type<T, U>
{
static constexpr bool signed_xor = std::is_signed<T>{} xor std::is_signed<U>{};
//base case: forwards to the satandard library
template<bool b = signed_xor, typename = void>
struct helper :
std::common_type<T, U>
{};
//mixed signed/unsigned: forwards to the case involving two basic_ranged_integer
template<typename _ >
struct helper<true, _> :
common_type<typename make_ranged_integer<T>::type, typename make_ranged_integer<U>::type>
{};
using type = typename helper<>::type;
};
在上面, make_ranged_integer
应该采用原始整数类型,并将type
定义为所需的对应basic_ranged_integer
。
这是一个可能的实现:
#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;
}
也许我错过了一些东西,但是你不想在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;
};
重复这个`long
, long long
等 ...而common_type
的模板定义将处理只有一个ranged_integer
类型的所有可变参数的情况?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.