关于C参数列表的隐式类型转换是如何工作的我很困惑.特别是,我有一些函数,如inRange(x,start,end),它们返回一个bool,具体取决于x是否在start和end之间.
[在这个描述中,范围只是(x> start&& x< end)的语法糖 – 当x是一个长字符串或一个昂贵的函数时它仍然很好 – 但在实际代码中有额外的用于处理边界的开放/封闭性质的方法. 我对上面的类型很模糊.特别是整数和浮点比较有不同的实现,这意味着模板不是很合适,因为没有C语言分组将int / long / unsigned / size_t等区别于float / double等等.所以我尝试过通过使用足够宽的int / float类型定义两个版本的inRange来使用类型系统:
inline bool inRange(long x, long start, long end)
inline bool inRange(double x, double start, double end)
这不会“长久”或类似,但我们的代码最多只使用双倍和长.所以它看起来很安全:我希望inRange(int,long,long)等会隐式地将int转换为long,一切都会好的.但是,如果为浮点比较(我想要允许)写入双字的双字,则例如, inRange(mydouble,10,20),我还必须添加一堆显式转换来摆脱编译器警告并确保使用浮点比较:
inline bool inRange(double value, long low, long high) {
return inRange(value, (double)low, (double)high);
}
inline bool inRange(double value, double low, long high) {
return inRange(value, low, (double)high, lowbound, highbound);
}
...
不太好 – 我希望将long转换为double会自动/隐含 – 但是没问题.但是下一个发现真的搞砸了我:我的编译器遇到了一个带有三个整数(不长)的inRange作为参数,并说:
call of overloaded ‘inRange(int&, int&, int&)’ is ambiguous
然后是到目前为止定义的所有inRange函数的列表!所以C没有(int,int,int)arg列表的首选项(长,长,长)而不是(double,double,double)?真?
任何让我脱离这个洞的帮助都会非常感激……我从来没有想过这么简单的东西,只涉及原始类型可能会变得如此难以解决.使用所有可能的数字类型组合制作全套~1000三个arg函数签名并不是我希望的答案!
最佳答案 模板是这里的基础知识,你只需要一些SFINAE.
#include <limits>
#include <utility>
template <typename T>
struct is_integral {
static bool const value = std::numeric_limits<T>::is_integer;
};
template <typename Integral, typename T>
typename std::enable_if<is_integral<Integral>::value, bool>::type
inRange(Integral x, T start, T end) {
return x >= static_cast<Integral>(start) and x <= static_cast<Integral>(end);
}
template <typename Real, typename T>
typename std::enable_if<not is_integral<Real>::value, bool>::type
inRange(Real x, T start, T end) {
return x >= static_cast<Real>(start) and x <= static_cast<Real>(end);
}
理论上,我们可以更宽松,只允许开始和结束有不同的类型.如果我们想.
编辑:更改为只要有一个真实版本切换到真实版本,内置完整性检查.
#include <limits>
#include <utility>
#include <iostream>
template <typename T>
struct is_integral {
static bool const value = std::numeric_limits<T>::is_integer;
};
template <typename T>
struct is_real {
static bool const value = not is_integral<T>::value;
};
template <typename T, typename L, typename R>
struct are_all_integral {
static bool const value = is_integral<T>::value and
is_integral<L>::value and
is_integral<R>::value;
};
template <typename T, typename L, typename R>
struct is_any_real {
static bool const value = is_real<T>::value or
is_real<L>::value or
is_real<R>::value;
};
template <typename T, typename L, typename R>
typename std::enable_if<are_all_integral<T, L, R>::value, bool>::type
inRange(T x, L start, R end) {
typedef typename std::common_type<T, L, R>::type common;
std::cout << " inRange(" << x << ", " << start << ", " << end << ") -> Integral\n";
return static_cast<common>(x) >= static_cast<common>(start) and
static_cast<common>(x) <= static_cast<common>(end);
}
template <typename T, typename L, typename R>
typename std::enable_if<is_any_real<T, L, R>::value, bool>::type
inRange(T x, L start, R end) {
typedef typename std::common_type<T, L, R>::type common;
std::cout << " inRange(" << x << ", " << start << ", " << end << ") -> Real\n";
return static_cast<common>(x) >= static_cast<common>(start) and
static_cast<common>(x) <= static_cast<common>(end);
}
int main() {
std::cout << "Pure cases\n";
inRange(1, 2, 3);
inRange(1.5, 2.5, 3.5);
std::cout << "Mixed int/unsigned\n";
inRange(1u, 2, 3);
inRange(1, 2u, 3);
inRange(1, 2, 3u);
std::cout << "Mixed float/double\n";
inRange(1.5f, 2.5, 3.5);
inRange(1.5, 2.5f, 3.5);
inRange(1.5, 2.5, 3.5f);
std::cout << "Mixed int/double\n";
inRange(1.5, 2, 3);
inRange(1, 2.5, 3);
inRange(1, 2, 3.5);
std::cout << "Mixed int/double, with more doubles\n";
inRange(1.5, 2.5, 3);
inRange(1.5, 2, 3.5);
inRange(1, 2.5, 3.5);
}
运行于ideone:
Pure cases
inRange(1, 2, 3) -> Integral
inRange(1.5, 2.5, 3.5) -> Real
Mixed int/unsigned
inRange(1, 2, 3) -> Integral
inRange(1, 2, 3) -> Integral
inRange(1, 2, 3) -> Integral
Mixed float/double
inRange(1.5, 2.5, 3.5) -> Real
inRange(1.5, 2.5, 3.5) -> Real
inRange(1.5, 2.5, 3.5) -> Real
Mixed int/double
inRange(1.5, 2, 3) -> Real
inRange(1, 2.5, 3) -> Real
inRange(1, 2, 3.5) -> Real
Mixed int/double, with more doubles
inRange(1.5, 2.5, 3) -> Real
inRange(1.5, 2, 3.5) -> Real
inRange(1, 2.5, 3.5) -> Real