20

common_type<long, unsigned long>::typeこれはunsigned long、整数昇格後のオペランドに関して、標準が述べているためです...

[...]符号なし整数型を持つオペランドのランクが他のオペランドの型のランク以上の場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます

整数昇格システムをバグと呼ぶわけではありませんが、符号付きオペランドと符号なしオペランドの両方の範囲を表すことができる、より大きな符号付き整数型がある場合は、それを使用する必要があるようです。

一部のプラットフォームでは long == long long になる可能性があることを知っています。その場合、上記のルールが有効になります。しかし、より大きな符号付き整数型利用可能であれば、それを使用すべきではないでしょうか?

4

1 に答える 1

7

まず第一に、std::common_type (そしてもちろん boost::type_traits::common_type) は三項演算子を使用して型の結果を取得します。この場合、関連する引用はCppReferenceから取得されます, 6b)

E2 と E3 は算術型または列挙型です。通常の算術変換が適用されて共通の型になり、その型が結果になります。

この情報を使用して、通常の算術変換の規則をc++ 標準、5p10、88 ページで見つけることができます。

— それ以外の場合、符号なし整数型を持つオペランドのランクが他のオペランドの型のランク以上である場合、符号付き整数型を持つオペランドは、符号なし整数型を持つオペランドの型に変換されます。

したがって、基本的にあなたの質問への答えは次のとおりです。 ...標準がそう言っているからです。

しかし、この動作が予想外であると感じているのはあなただけではありません。実行可能な簡単な例を次に示します。

#include <iostream>
#include <typeinfo>
#include <type_traits>

int main(int argc, const char* argv[])
{

    std::cout << typeid(std::common_type<char, unsigned char>::type).name() << std::endl;
    // I would expect "short", and the result is "int", ok so far.

    std::cout << typeid(std::common_type<short, unsigned short>::type).name() << std::endl;
    // I would expect "int", and the result is "int", yay.

    std::cout << typeid(std::common_type<int, unsigned int>::type).name() << std::endl;
    // I would expect "long", but the result is "unsigned int"

    std::cout << typeid(std::common_type<long, unsigned long>::type).name() << std::endl;
    // I would expect "long long", but the result is "unsigned long"


    // So this usual arithmetic conversion can lead to unexpected behavior:
    auto var_auto = true ? var_i : var_ui;
    std::cout << typeid(var_auto).name() << std::endl;   // unsigned int
    std::cout << var_auto << std::endl;                  // 4294967173

    return 0;
}

しかし、現在の動作が問題であることはわかっており、いくつかの驚きを取り除く提案が存在します。

-ハンネス

于 2013-03-13T15:04:39.213 に答える