ここに簡単なコードがあります:
#include <iostream>
#include <cstdint>
int main()
{
const unsigned char utf8_string[] = u8"\xA0";
std::cout << std::hex << "Size: " << sizeof(utf8_string) << std::endl;
for (int i=0; i < sizeof(utf8_string); i++) {
std::cout << std::hex << (uint16_t)utf8_string[i] << std::endl;
}
}
ここでは、MSVCとGCCで異なる動作が見られます。 MSVCは"\xA0"
をエンコードされていないUnicodeシーケンスとして認識し、それをutf-8にエンコードします。したがって、MSVCでは、出力は次のようになります。
C2A0
Utf8ユニコードシンボルU+00A0
で正しくエンコードされています。
しかし、GCCの場合は何も起こりません。文字列を単純なバイトとして扱います。文字列リテラルの前にu8
を削除しても変更はありません。
両方のコンパイラは、文字列がC2A0
に設定されている場合、出力u8"\u00A0";
でutf8にエンコードします。
コンパイラの動作が異なるのはなぜですか?
テストに使用したソフトウェア:
GCC 8.3.0
MSVC 19.00.23506
C++ 11
どちらも間違っています。
私の知る限り、C++ 17標準は here と言っています:
ナローストリングリテラルのサイズは、エスケープシーケンスおよびその他の文字の総数に、各ユニバーサル文字名のマルチバイトエンコーディング用に少なくとも1つ、および終了 '\ 0'用に1つを加えたものです。
他にもヒントがありますが、これはエスケープシーケンスがマルチバイトではなく、MSVCの動作が間違っていることを示す最も強い兆候であるようです。
現在、調査中としてマークされているチケットがあります。
ただし、UTF-8リテラルについて here とも記載されています。
値が単一のUTF-8コード単位で表現できない場合、プログラムは不正な形式です。
0xA0
は有効なUTF-8文字ではないため、プログラムはコンパイルできません。
ご了承ください:
u8
で始まるUTF-8リテラルは、ナローとして定義されています。\xA0
はエスケープシーケンスです\u00A0
は、エスケープシーケンスではなく、ユニバーサル文字名と見なされますコンパイラの動作が異なるのはなぜですか?
C++標準の実装を決定した方法により、コンパイラーの動作は異なります。
したがって、GCCで失敗したものは、より許容範囲が広いため、通常はMSVCで機能します。そして、MSVCはこれらの問題のいくつかを自動的に処理します。
同様の例を次に示します: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=33167 。それは標準に従いますが、あなたが期待するものではありません。
どちらが正しいかについては、「正しい」の定義が何であるかに依存します。
どちらの方法が標準に当てはまるかはわかりません。
MSVCが行う方法は、少なくとも論理的に一貫しており、簡単に説明できます。 3つのエスケープシーケンス\x
、\u
、\U
は、入力から取得する16進数字の数(2、4、または8)を除いて、同じように動作します。それぞれがUnicodeコードポイントを定義し、その後、UTF-8にエンコードする必要があります。エンコードせずにバイトを埋め込むと、無効なUTF-8シーケンスが作成される可能性があります。