我很担心隐式类型转换在C ++参数列表方面的工作原理。特别是,我有一些函数,如inRange(x,start,end),它们返回一个bool,具体取决于x是否在start和end之间。
[在此描述中,范围只是(x> start&& x< end)的语法糖 - 当x是长字符串或昂贵的函数时仍然很好 - 但在实际代码中有额外的args来处理边界的开放/封闭性质。]
我对上面的类型含糊不清。特别是整数和浮点比较有不同的实现,这意味着模板不是很合适,因为没有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函数签名并不是我希望的答案!
答案 0 :(得分:2)
模板是这里的基础知识,你只需要一些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);
}
理论上,我们甚至可以更多宽松,只允许start
和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
答案 1 :(得分:0)
(懒惰的方法:)使用函数模板 - 让编译器担心它..
template <typename T1>
inline bool inRange(T1 x, T1 start, T1 end)
{
// do stuff
}
这意味着您可以传入支持operator<
的任何对象...并且针对您要执行其他操作的特定类型(例如std::string
,const char*
等)重载