型名に ()
をつける関数スタイルのキャストで、MSVC でビルドできていた次のコードが gcc でエラーになると質問を受けました。
なぜコンパイルエラーになるのか即答できなかったので規格にあたりました。
# include <iostream>
int main()
{
char c = 'a';
auto uc = unsigned char(c);
std::cout << uc << std::endl;
return 0;
}
$ g++ -std=c++14 temp.cpp
temp.cpp: In function ‘int main()’:
temp.cpp:6:12: error: expected primary-expression before ‘unsigned’
auto uc = unsigned char(c);
^~~~~~~~
結論
gcc が正しい。 MSVC は規格通りの挙動をしていない。
関数形式の明示的型変換
そもそも int x = int(0);
のように 型名()
の形式でキャストすることを関数形式の明示的型変換(Explicit type conversion (functional notation))と規格で呼んでいます。
C++14規格書(N4140) の 5.2.3 Explicit type conversion (functional notation) を見ると、
[C++11: 5.2.3/1]: A simple-type-specifier (7.1.6.2) or typename-specifier
(14.6) followed by a parenthesized expression-list constructs a value of
the specified type given the expression list.
とあり型名には simple-type-specifier か typename-specifier が使えると書いています。
typename-specifier は typename
キーワードを使うのでここでは関係ありません。
つまり unsigned char
は simple-type-specifier かどうかが焦点になります。
simple-type-specifier とは何かを調べてみると 7.1.6.2 に定義があります。
[C++11: 7.1.6.2/1]: The simple type specifiers are
simple-type-specifier:
...(省略)...
type-name:
...(省略)...
decltype-specifier:
...(省略)...
省略した定義部分には unsigned char
はありません。また 7.1.6.2 の Table 10 - simple-type-specifiers and the types they specify には単数形ではない simple-type-specifier(s) の例として unsigned char
が書かれています。
つまり unsigned char
や long long
は simple-type-specifier(s) であり simple-type-specifier ではないため関数形式の明示的型変換では使えません。
エラーになる gcc が正しいです。MSVC は妙な Microsoft 拡張をしてるんでしょう。
結局どうすれば?
MSVC を使わない
そもそも関数形式の明示的型変換を使わず static_cast を使う。
参考
cppreference: Declarations
https://en.cppreference.com/w/cpp/language/declarations
C++11の文法と機能(C++11: Syntax and Feature)
5.5.3 関数形式の明示的型変換(Explicit type conversion (functional notation))
http://ezoeryou.github.io/cpp-book/C++11-Syntax-and-Feature.xhtml#expr.type.conv