C++;参数列表中的隐式类型转换 我对隐式类型转换如何处理C++参数列表感到困惑。特别是,我有一系列函数,比如inRange(x,start,end),它们根据x是否在start和end之间返回bool

C++;参数列表中的隐式类型转换 我对隐式类型转换如何处理C++参数列表感到困惑。特别是,我有一系列函数,比如inRange(x,start,end),它们根据x是否在start和end之间返回bool,c++,types,arguments,type-conversion,implicit-conversion,C++,Types,Arguments,Type Conversion,Implicit Conversion,[在此描述中,inRange只是(x>start&&x

[在此描述中,inRange只是(x>start&&x 我对上面的类型含糊不清。特别是,对于整数和浮点比较有不同的实现,这意味着模板不是真正合适的,因为没有C++语言组,它区分了INT/长/无符号/ SsieZiT等。因此,我尝试使用类型系统,通过定义两个具有足够宽的int/float类型的inRange版本:

inline bool inRange(long x, long start, long end)
inline bool inRange(double x, double start, double end)
这不会捕获“long-long”或类似的内容,但我们的代码最多只使用double和long。所以它看起来很安全:我希望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的转换是自动/隐式的——但是可以。但下一个发现真的让我大吃一惊:我的编译器遇到了一个以三个int(而不是long)作为参数的inRange,并说:

call of overloaded ‘inRange(int&, int&, int&)’ is ambiguous
后面是到目前为止定义的所有inRange函数的列表!所以C++没有偏好(int,int,int)的ARG列表要用(长,长,长)来解决,而不是(双,双,双)?真的吗

如果能帮我走出这个洞,我将不胜感激。。。我从来没有想到如此简单,只涉及原始类型的事情会变得如此难以解决。用所有可能的数字类型组合制作全套约1000个三arg函数签名并不是我所希望的答案

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

template <typename T1>
inline bool inRange(T1 x, T1 start, T1 end)
{
  // do stuff
}
模板
内联布尔值范围(T1 x,T1开始,T1结束)
{
//做事
}

这意味着您可以传入任何支持
操作符的对象。模板是这里的基础,您只需要一些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);
}
运行时间:


为了解决歧义,使用类型标识符,比如长的,将是:50L,无符号int:50u,浮点:50。f,等等,但是,我猜只能解决常数的情况,而不是变量…模板不是真的合适,因为没有C++语言组,它区分了int /长/无符号/ siZext等。虽然这在技术上是正确的,但在实践中,为一般情况实现一个模板并为特定情况重载相同的函数是微不足道的,正如Nim在下面的回答中指出的那样。这意味着在您为浮点定义的特定情况之外,将使用通用版本(这将消除歧义)。@Justinᚅᚔᚈᚄᚒᚔ: 事实并非如此<代码>标准::数值限制
有一个
是整数
请参见。@Justinᚅᚔᚈᚄᚒᚔ: 我不会冒险走那条路。在标准头中实现的许多东西(
是可复制的)不能在“纯C++中定义。因此,我认为标准库是语言本身的一部分;Boost是一个扩展;)出于实际考虑,您会说“特别是整数和浮点比较有不同的实现”。如果它们不同,则使用不同的函数名进行整数和浮点比较。问题解决了!这实际上是我们已经使用了很长时间的实现,但是它与x、start、end的混合类型有关。也许这可以小心解决,但在几次尝试建立专业化等之后,我试图回到未经实践的方式。。。这就产生了这个问题!不过,谢谢:)@andybuckley,如果存在混合类型,那么您需要将每种类型都设置为模板参数,例如,请参见:我有一种感觉,当尝试部分专门为每个模板参数指定双精度,或为所有模板参数指定长精度时,我们最终会回到模糊问题。我将尝试@Matthieu M的方式,因为SFINAE基本上是我在这里试图处理的问题。。。如果我在这方面没有成功,我将重温这个已经走过的路,看看你的建议是否适用于我们所有的用例:编译依赖于此的大量代码是找到我们没有想到的情况的一个令人烦恼的好方法……谢谢Matthieu,这让我了解了一大堆我不知道的东西!但是,从逻辑上讲,如果三个参数(value/start/end)中的任何一个是实值的,我想启用实值比较:是否有一种合理的方法可以用enable_if的第一个模板arg中的逻辑组合来表示这一点?e、 g.std::enable_是否有效?(希望我隐式重命名的模板参数是不言而喻的)@andybuckley:是的,完全是。可以使用常规布尔运算符操纵常量布尔值,以形成一个新的不断求值的布尔值,没有问题。但是,您需要为“Integral”情况提供相反的条件,以避免同时启用这两个条件,这将触发歧义。我会编出来的,注意编辑;)
#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);
}
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