17

次のプログラムがあるとします。

#include <cmath>

int main()
{
    std::abs(0u) ;
}

gccこれが不適切でclangあるかどうかについて意見が分かれています。コード ビルドを使用するgcclibstdc++、エラーや警告が表示されずにビルドされます (ライブを参照してください) 。clanglibc++

error: call to 'abs' is ambiguous
std::abs(0u) ;
^~~~~~~~

どの結果が正しいですか? abs(0u)あいまいである必要がありますか?


MSalters は興味深い関連する質問を指摘しています:テンプレート バージョンの std::abs

4

1 に答える 1

17

正しいように見えlibstdc++ますが、これは形式が正しくありませんが、これが LWG アクティブな問題の欠陥であるかどうかについて、いくつかの疑問が表明されていることがわかります2192

ドラフト C++11 標準セクション26.8 [c.math]パラグラフ11には、次のように記載されています。

さらに、次のことを保証するのに十分な追加のオーバーロードがあるものとします。

次の項目が含まれます。

  1. それ以外の場合、double パラメーターに対応するいずれかの引数が double 型または整数型である場合、double パラメーターに対応するすべての引数は実質的に double にキャストされます。

libstdc++そして、これが実際にこのケースを提供していることがわかります。

template<typename _Tp>
inline typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
                                                  double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }

llabs が存在しない場合、std::abs (long long) が std::abs (double) に頼るgccというバグ レポートもあります。

[...]標準では問題ありません。整数は無条件に double になるはずです。[...]

バグ レポートは、最終的にLWG アクティブな問題 2192 につながります: std::abs(0u) の有効性と戻り値の型が不明です

  1. C++11 では、26.8 [c.math] p11 (LWG 2086 も参照) からの追加の「十分なオーバーロード」規則が、std::abs() オーバーロードにも適用可能であると読み取ることができます。考えられる結論:

プログラム

    #include <type_traits>
    #include <cmath>

    static_assert(std::is_same<decltype(std::abs(0u)), double>(), "Oops");

    int main() {
      std::abs(0u); // Calls std::abs(double)
    }

26.8 [c.math] p11 のサブブレット 2 (「[..] または整数型 [..]」) のため、適切な形式である必要があります (LWG 2086 の現在の解像度はそうではないことに注意してください)。この問題を修正します)。

  1. と の両方を含む変換単位は、オーバーロード std::abs(int) の戻り値の型に対する 2 つの競合する要件のために、形式が正しくない可能性があります。

少なくとも 2 番目の結果は意図されていないように思われます。個人的にはどちらも残念だと思います [...] また、7.25 p2+ で C99/C1x から対応する「ジェネリック型関数」ルール セットが追加されたことにも注意する必要があります。 3 は と の浮動小数点関数に制限されているため、abs 関数には適用できません (ただし fabs 関数には!)。

問題は、これがabs同様に適用されることを意図していたかどうかです。現在の文言を解釈して除外する方法がないように見えるため、これは欠陥である可能性がありabsます。

したがって、現在の文言は準拠していることを示していますが、現在の実装をそのまま選択しlibstdc++た理由は明らかではありません。libc++このトピックに関するバグ レポートや議論は見つかりませんでした。また、LWG の問題では実装の相違について言及されていません。

提案された解決策は、std::abs(0u)形式が正しくありません。

abs() が、整数昇格 ([conv.prom]) によって int に変換できない符号なし整数型の引数で呼び出された場合、プログラムは不正な形式です。[注: int に昇格できる引数は、C との互換性のために許可されています — 末尾の注]

unsigned 型を使用するという概念に疑問を呈する人もいるかもしれませんabsが、ハワード・ヒンナントはレポートで、テンプレートを使用する場合、そのような結果は明らかではない可能性があると指摘し、例を示しています。

[...]特に、テンプレートがある C++ では、関連する型が設計時にプログラマーに常に明らかであるとは限りません。たとえば、次のことを考慮してください。

template <class Int>
Int
analyze(Int x, Int y)
{
  // ...
  if (std::abs(x - y) < threshold)
  {
    // ...
  }
  // ...
}
于 2015-04-20T14:39:55.233 に答える