繁体   English   中英

参数列表中的C ++隐式类型转换

[英]C++ implicit type conversion in argument lists

我对C ++参数列表的隐式类型转换如何工作感到困惑。 特别是,我有一堆称为inRange(x,start,end)的函数,它们根据x是否在start和end之间返回布尔值。

[在此描述中,inRange只是(x> start && x <end)的语法糖-当x是一个长字符串或一个昂贵的函数时,它仍然很不错-但在实际代码中,还有一些用于处理open的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)

这不会捕获“ long long”或类似的东西,但是我们的代码最多只使用double和longs。 因此它看起来很安全:我希望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,带有三个int(不是long)作为参数,并说:

call of overloaded ‘inRange(int&, int&, int&)’ is ambiguous

然后是到目前为止定义的所有 inRange函数的列表! 因此,C ++没有优先选择使用(long,long,long)而不是(double,double,double)来解析(int,int,int)arg列表吗? 真?

任何使我摆脱困境的帮助都将不胜感激……我从来没有想到过,仅涉及原始类型的简单事情就变得如此难以解决。 用所有可能的数值类型组合制作全套〜1000个三参数函数签名不是我想要的答案!

模板是这里的基础,您只需要一些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);
}

从理论上讲,我们可以更加宽容,只允许startend具有不同的类型。 如果我们想。

编辑 :更改为一旦有真实版本,便立即切换到真实版本,并具有内置的健全性检查功能。

#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

(惰性方法:)使用函数模板-让编译器担心它。

template <typename T1>
inline bool inRange(T1 x, T1 start, T1 end)
{
  // do stuff
}

这意味着您可以传入任何支持operator< ...的对象,并在想要执行某些operator<的特定类型上重载(例如std::stringconst char*等)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM