我已经定义了一个充当整数的类型。我想为我的类型定义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)
common_type
和int
的{{1}}为unsigned
。例如:http://ideone.com/9IxKIW。 Standardese可以在§20.9.7.6/3中找到,其中两个值的unsigned
是
common_type
在§5.16/ 6中,它说
第二个和第三个操作数具有算术或枚举类型;该 通常的算术转换被执行以使它们成为共同的 类型,结果是那种类型。
通常的算术转换在§5/ 9中定义为
否则,如果具有无符号整数类型的操作数具有等级 大于或等于另一个操作数类型的等级, 带有符号整数类型的操作数应转换为类型 带有无符号整数类型的操作数。
答案 0 :(得分:3)
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
。我想你可以用min
和max
来完成。
如果您的班级支持继承,您也可以制作is_ranged_integer
特征。
不要忘记将您的库放在命名空间内。
答案 1 :(得分:2)
简短回答
如果绝对需要使用标准库提供的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
正确地推断出范围,因此只有成对{{} { {1}}的种类前述common_type
生成[long [long]] unsigned
。这使得我们只有六个情况下,我们要解决办法,即̶̶[long [long]] int
̶及其排序̶p̶e̶r̶m̶u̶t̶a̶t̶i̶o̶n̶s̶.̶̶(I”中号忽略固定宽度的类型以下,̶但延伸的想法,认为它们应该是直接的)̶
W̶e̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶̶:̶
实际上我们无法根据 n3485
[meta.type.synop]第1段
“除非另有说明,否则为本子条款[̶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̶
中定义的]任何类模板添加特殊化的程序的行为是未定义的。”
[meta.trans.other]表57
[...] A
如果特化中的至少一个模板参数是用户定义的类型,则程序可以专门化该特征[template <class... T> common_type
]。 [...] “
这意味着没有有效的方法来覆盖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
。
答案 2 :(得分: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;
}
答案 3 :(得分: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
类型?