2017-11-12 51 views
4

キーをconstexprにしてstd :: mapを初期化したい。コードが生成されたバイナリは、キータイプの文字列が含まれています最近どの打ち鳴らすとgcc、コンパイルしている間std :: mapの初期化にC++ 11のconstexprを使用する

#include <map> 
using std::map; 

constexpr unsigned int str2int(const char* str, const int h = 0) { 
    return !str[h] ? 5381 : (str2int(str, h + 1) * 33)^str[h]; 
} 

const map<unsigned int, const char*> values = { 
    {str2int("foo"), "bar"}, 
    {str2int("hello"), "world"} 
}; 

int main() { return 0; } 

:次のC++ 11 MWEを考えてみましょう

C String Literals

をしている理由バイナリに含まれているキーはconstexprとして使用されていますか?この現象を回避する方法はありますか?

もちろん、マップの初期化は実行時に行われます。しかし、コンパイル時にバイナリの値をconstexprに置き換えてはいけませんか?

注:これはもちろん簡単な例です。私は異なったブーストの構造があることを知っています。このユースケースにはより適しています。私は特にに興味があり、なぜこれが起こっているのですか?

[編集]

この現象は、最適化が有効かどうかに関係なく発生します。 次のコードは、バーは、文字列テーブル内の唯一のユーザー定義の文字列であることをコンパイルします。

#include <map> 
#include <iostream> 
#include <string> 

using namespace std; 

constexpr unsigned int str2int(const char* str, const int h = 0) { 
    return !str[h] ? 5381 : (str2int(str, h + 1) * 33)^str[h]; 
} 

int main() { 
    string input; 
    while(true) { 
    cin >> input; 
    switch(str2int(input.c_str())) { 
     case str2int("quit"): 
     return 0; 
     case str2int("foo"): 
     cout << "bar" << endl; 
    } 
    } 
} 

私は

$ for x in "gcc-mp-7" "clang"; do 
    $x --version|head -n 1 
    $x -lstdc++ -std=c++11 -Ofast constexpr.cpp -o a 
    $x -lstdc++ -std=c++1z -Ofast constexpr.cpp -o b 
    strings a|grep hello|wc -l 
    strings b|grep hello|wc -l 
done 

gcc-mp-7 (MacPorts gcc7 7.2.0_0) 7.2.0 
     1 
     0 
Apple LLVM version 8.1.0 (clang-802.0.38) 
     1 
     0 
+2

constexprは、コンパイラがコンパイル時に結果を使用できることを意味します。しかし、それはすべての評価がコンパイル時に行われなければならないという意味ではありません。 C++には、コンパイル時の評価が非常に難しい可能性がありません。コンパイラの中には、「式が複雑になる」と不平を言うものがあります... – Klaus

+2

最適化でコンパイルしましたか? [clang](https://godbolt.org/g/AzZeMg)には結果として得られるアセンブリのキーがありません。 – Rakete1111

+0

編集 – muffel

答えて

1

私は再現できない、小さなシェルスクリプトを使用していた結果を確認するにはどちらもありませんg ++(トランク)またはclang ++(トランク)。私は次のフラグを使用しました:-std=c++1z -Ofast。コンパイルしたバイナリの内容をstringsで確認しました。"foo"または"hello"はありませんでした。

最適化を有効にしてコンパイルしましたか?

str2intを使用しても、コンパイル時の評価は行われません。それを強制するために、あなたが行うことができます:

constexpr auto k0 = str2int("foo"); 
constexpr auto k1 = str2int("hello"); 

const map<unsigned int, const char*> values = { 
    {k0, "bar"}, 
    {k1, "world"} 
}; 
+0

私はあなたが言っていることを試しましたが、 '--std = C++ 1z'オプションが私のコードを同様に修正しているようです。残念ながら、質問に記載されているように、問題が発生している特定のプロジェクトにC++ 11を使用する必要があります。任意のアイデアをC++ 11で修正する方法は? – muffel

+1

コードを「修正」するのは「高速」ではないのですか? –

+0

はい、macOS 10.13.1でgcc 7.2.0とclang-802.0.38を使って再度試しました。私が '--std = C++ 1z'を使用した場合、どちらもキーストリングを省略します。 – muffel

1

打ち鳴らす5.0またはMSVC 17

DEMO

あなたは(上のデバッグシンボルで構築している、GCC 7.2で--std=c++11 -O2を使用して、問題を再現することはできません-g)?それはあなたが見ているものかもしれません。

+0

が奇妙です。多分私は何かを欠いているでしょうか? – muffel

+0

'-O2'を試したら? 'objdump -sghCd'は何を出力しますか? – rustyx

0
template<unsigned int x> 
using kuint_t = std::integral_constant<unsigned int, x>; 

const map<unsigned int, const char*> values = { 
    {kuint_t<str2int("foo")>::value, "bar"}, 
    {kuint_t<str2int("hello")>::value, "world"} 
}; 

コンパイル時の評価が必要です。

ではそれは少し少ない冗長です:

template<unsigned int x> 
using kuint_t = std::integral_constant<unsigned int, x>; 
template<unsigned int x> 
kuint_t<x> kuint{}; 

const map<unsigned int, const char*> values = { 
    {kuint<str2int("foo")>, "bar"}, 
    {kuint<str2int("hello")>, "world"} 
}; 

で:

template<auto x> 
using k_t = std::integral_constant<std::decay_t<decltype(x)>, x>; 
template<auto x> 
k_t<x> k{}; 

const map<unsigned int, const char*> values = { 
    {k<str2int("foo")>, "bar"}, 
    {k<str2int("hello")>, "world"} 
}; 

それはタイプ固有のバージョンなしで最も原始的なタイプ定数で動作します。

関連する問題