13

私は gcc 4.7.2 でユーザー定義の定数をいじっていて、よくわからない何らかのサイズ制限要因に遭遇しました。

アイデアは、固定小数点 10 進数型の constexpr 演算子 "" を定義することでした。double からのキャストを避けたいのですが、可変個引数テンプレートを使用して、コンパイル時に仮数と指数を解析します。仮数部の解析は少しトリッキーでした。

以下のコードの下部にある 3 つの無効な行のいずれかを有効にすると、gcc が無限ループに陥り、そこでハングします。浮動小数点リテラルと可変個引数テンプレートの明示的なインスタンス化の最大サイズは同じですが、整数リテラルのサイズはわずかに大きいことに気付きました。

コマンドを使用しました: g++ -std=c++11 -Wall -g -oliteral_valueliteral_value.cpp

-ftemplate-depth-128 を使用しても違いはありません。

#include <iostream>
#include <cstdint>

typedef std::uint64_t value_type;

template<value_type Temp, char... List> struct literal_parser;

template<value_type Temp, char Head, char... List>
struct literal_parser<Temp, Head, List...>
{
    static const value_type value = Head == '.' ?
        literal_parser<Temp, List...>::value :
        literal_parser<Temp * 10 + Head - '0', List...>::value;
};

template<value_type Temp, char Last>
struct literal_parser<Temp, Last>
{
    static const value_type value = Last == '.' ?
        Temp : Temp * 10 + Last - '0';
};

template<char... List>
inline constexpr value_type operator"" _value() noexcept
{
    return literal_parser<0U, List...>::value;
}

int main()
{
    std::cout << "value 1: " << 123456789012345678_value << std::endl;
    std::cout << "value 2: " << 1.23456789012345_value << std::endl;
    std::cout << "value 3: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value << std::endl;

#if 0
    std::cout << "value 4: " << 1234567890123456789_value << std::endl;
    std::cout << "value 5: " << 1.234567890123456_value << std::endl;
    std::cout << "value 6: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value << std::endl;
#endif
}

それはgccのバグですか、それとも何か不足していますか?

4

4 に答える 4

3

私はあなたがコンパイラを狂わせるいくつかの素晴らしいコーナーケースを見つけたと言わなければなりません:-)私にとってgcc4.7.2と4.8はコンパイル中にクラッシュしました。ただし、clang(最上位バージョン)はコード全体を正常にコンパイルしましたが、2.4GBのRAMを使用していました。この問題は、「。」のternaty演算子に関連しているようです。小切手。それを削除し、main()で実数テストにコメントすると、すべてが爆風のように正常にコンパイルされます。

したがって、あなたの質問に答えることはおそらく何も見逃すことはなく、gccとclangはおそらくあなたのケースに基づいてそれらの実装を修正する必要があります。

于 2012-11-14T17:21:22.537 に答える
2

Mateusz の回答に基づいて、constexpr 関数を使用してliteral_parser テンプレートを再定義し、1 桁を解析したところ、すべてが完璧に見えました。助けてくれてどうもありがとう!

#include <iostream>
#include <cstdint>

typedef std::uint64_t value_type;

template<value_type Temp, char... List> struct literal_parser;

inline constexpr value_type parse_digit(value_type value, char digit) noexcept
{
    return digit == '.' ? value : value * 10 + digit - '0';
}

template<value_type Temp, char Head, char... List>
struct literal_parser<Temp, Head, List...>
{
    static const value_type value =
        literal_parser<parse_digit(Temp, Head), List...>::value;
};

template<value_type Temp, char Last>
struct literal_parser<Temp, Last>
{
    static const value_type value = parse_digit(Temp, Last);
};

template<char... List>
inline constexpr value_type operator"" _value() noexcept
{
    return literal_parser<0U, List...>::value;
}

int main()
{
    std::cout << "value 1: " << 123456789012345678_value << std::endl;
    std::cout << "value 2: " << 1.23456789012345_value << std::endl;
    std::cout << "value 3: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value << std::endl;

    std::cout << "value 4: " << 1234567890123456789_value << std::endl;
    std::cout << "value 5: " << 1.2345678901234567890_value << std::endl;
    std::cout << "value 6: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value << std::endl;
}
于 2012-11-14T17:47:14.300 に答える
1
static const value_type value = Head == '.' ?
    literal_parser<Temp, List...>::value :
    literal_parser<Temp * 10 + Head - '0', List...>::value;

これにより、コンパイラは条件の両側を評価する必要があるため、コンパイル時間が完全に爆発的に増加します(全体を桁数で指数関数的にします)。のように表現を変えてみてくださいliteral_parser<Head == '.' ? Temp : Temp * 10 + Head - '0', List...>::value

于 2012-11-15T06:28:39.123 に答える
-3

問題は、コマンドの作成方法にあると思います

g++ -std=c++11 -Wall -g -o literal_value literal_value.cpp

ソースファイルを最後に配置しないことをお勧めします。実際、これはWindowsのMinGWでg++4.7.2で正常にコンパイルされます。

g++ -std=c++11 -Wall -g literal_value.cpp -o literal_value

一般的には、仕様をリンクするために行の最後の部分を予約することをお勧めします。

于 2012-11-14T17:05:06.063 に答える