簡體   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