アンサイクロペディアのC++記事に有るHelloWorldは、C++11以降動かない
以下のソースコードです。
#include <iostream> template<char O, bool =O == 'h'? '-' :O == 'w'? '=' :0>class _; template<char O>std::ostream&operator<<(std::ostream&lhs,_<O,0>*) { return lhs << O; } template<char O>std::ostream&operator<<(std::ostream&lhs, _<(O), 'o'>*) { return lhs<<(char)(O - '!' + '.' - '-' ); } template<> std::ostream&operator<<<'~'>(std::ostream& m9,_<'~', 'o'>*) { return m9<<('m'); } int main(int orz=3) { std::cout<<(_<'h'>*)0<<(_<'e'>*)0<<(_<'l'>*)0<<(_<'l'>*)0<<(_<'o'>*)0<<(_<'w',0>*)0<<(_<' '>*)0<<(_<'w'>*)0<<(_<'o'>*)0<<(_<'r'>*)0<<(_<'l'>*)0<<(_<'d'>*)0<<(_<'!'>*)0<<std::endl; return 0; }
なぜ
結論から先に記載してしまうと
- 非型テンプレート引数は定数式(constant expression)であり
- 定数式は縮小変換(narrowing conversion)を行うことが出来ないため。
となります。
非型テンプレート引数(typename Tやclass Tでない)は
C++規格で言う定数式(constant expression)であり
定数式は縮小変換(narrowing conversion)を行うことが出来ません。
確認
エラーになる箇所を確認してみます。
template<char O, bool =O == 'h'? '-' :O == 'w'? '=' :0>class _;
char Oが'h'または'w'の時
bool = '-' または '='として展開されますが。
'-'や'='は1を超過しているため(1 < '-')
bool代入は縮小変換に該当してしまい、エラーになります。
以下のソースコードも 同じ理由でエラーです。
template<char O>std::ostream&operator<<(std::ostream&lhs, _<(O), 'o'>*) { return lhs<<(char)(O - '!' + '.' - '-' ); } template<> std::ostream&operator<<<'~'>(std::ostream& m9,_<'~', 'o'>*) { return m9<<('m'); }
第2テンプレート引数に'o'が渡っており
上記と同じ縮小変換に該当してしまいます。
解決方法
解決方法を幾つか考えてみましたが
正直言ってイマイチです。
class _のデフォルト引数を0or1にするとか
template<char O, bool =O == 'h'? 1 :O == 'w'? 1 :0>class _{};
引数指定部では!=を挟むとか
template<char O>std::ostream&operator<<(std::ostream&lhs, _<(O), 'o' != 0>*) { // != 0を挟む return lhs<<(char)(O - '!' + '.' - '-' ); }
明示キャストを挟むとか
template<> std::ostream&operator<<<'~'>(std::ostream& m9,_<'~', static_cast<bool>('o')>*) { // 定数式変換前に明示的キャストを行う return m9<<('m'); }
うーん
#include <iostream> template<char O, bool =O == 'h'? 1 :O == 'w'? 1 :0>class _{}; template<char O>std::ostream&operator<<(std::ostream&lhs,_<O,0>*) { return lhs << O; } template<char O>std::ostream&operator<<(std::ostream&lhs, _<(O), 'o' != 0>*) { // != 0を挟む return lhs<<(char)(O - '!' + '.' - '-' ); } template<> std::ostream&operator<<<'~'>(std::ostream& m9,_<'~', static_cast<bool>('o')>*) { // 定数式変換前に明示的キャストを行う return m9<<('m'); } int main(int orz=3) { std::cout<<(_<'h'>*)0<<(_<'e'>*)0<<(_<'l'>*)0<<(_<'l'>*)0<<(_<'o'>*)0<<(_<'w',0>*)0<<(_<' '>*)0<<(_<'w'>*)0<<(_<'o'>*)0<<(_<'r'>*)0<<(_<'l'>*)0<<(_<'d'>*)0<<(_<'!'>*)0<<std::endl; return 0; }
問題を解決したは良いものの
元記事にあるユーモア性が失われてしまいました。
ユーモアのあるソースコードを書くのは難しいですね
参考
超参考にしたstackoverflow質問。10割答え
Narrowing int to bool in SFINAE, different output between gcc and clang
また、上の質問には書かれていませんが。
仕様書には、定数式での文脈上bool変換(contextually converted to bool)は
[expr.const]に記載された変換しか受け付けてくれない
とご丁寧に記載されています。
(英検100級なので翻訳に自身がない)
[expr.const] A contextually converted constant expression of type bool is an expression, contextually converted to bool, where the converted expression is a constant expression and the conversion sequence contains only the conversions above. https://eel.is/c++draft/expr.const
しかし、普通に考えて
何もbool変換まで、縮小変換NGにする必要は無かったのではと
考えざるを得ません。
この辺りの経緯や意図については
今後の宿題ですね。