2023年1月4日水曜日

継続的デリバリーのソフトウェア工学の感想

新年あけましておめでとうございます.
今年の抱負として,月に一冊は技術書の感想を書いてみようかな,と思い,長いこと更新していなかったブログを更新することにしました.

今回読んだのはこちら.
継続的デリバリーのソフトウェア工学(日経BOOKPLUS)

継続的デリバリーの,となっていますが,読んだ限りでは元のタイトルのModern Software Engineeringを実現する方法として継続的デリバリーも推している,という感じなのかな,という気がします.

序文では,この書籍は次のようなものだと述べています.

ソフトウェア工学に工学を取り戻す試み

つまり,現在のソフトウェア工学は工学になっていない,ということです.正確には,皆が誤解しているというところなのだと思います.実際,この書籍で紹介される多くの概念は過去に提唱されてきたものがほとんどです.

そのための流れとして,まず科学と工学の違い,そしてソフトウェア工学とはどうあるべきなのかが定義されます.そして,工学であるために必要となる学習と複雑さの管理 について説明し,最後にそれらのためのツールというかアイデアを紹介しています.

この書籍が良いところは,既に様々な書籍で紹介されているアイデアを整理し,それらがどう関係しあって良い結果をもたらすのか,ということが具体例も交えつつ解説されている点です.

例えば,テスト駆動開発(Test Driven Development, TDD)を用いると,疎結合な設計になりやすいであるとか,関心事の分離により変更が容易になるとかです.

TDDや継続的インテグレーション(Continuous Integration, CI),継続的デリバリー(Coninuous Delivery, CD)などは,良い設計をする方向への圧力となるのだと.

また,プログラマはどうしても個々人のレベルアップによる開発を夢見がちですが,それでは大規模化が困難であることにも触れています.ある程度の規模のソフトウェア開発に関わっている人ほど,その点については何となくでも気付いていると思います.

TDDとなると,どうしてもテストを書くのが面倒だと言う人は多いのではないでしょうか? しかし,この書籍を読めば,何故TDDが必要なのか,単純に単体テストではダメなのか,という質問をされた場合に返答できるようになると思います.

雑に要約すると,TDDでのテストは仕様を記述しているのであって,バグを検出することが第一の目的ではない,ということです.

他にも色々とありますが,この書籍ではTDDやCI,CD,他にはマイクロサービスやDevOpsなどによってもたらされる利点が語られていますが,そのものについては既知のものとして詳しく触れてはいません.

1つか2つソフトウェアの開発経験を積んだ後,まずはこの書籍を読み,知らない概念についてはそれぞれの参考文献を読む,という読み方をするのが良いと思います.

2020年7月20日月曜日

DirectX12の魔導書を読んで

DirectX12の魔導書は,翔泳社より出版されている,国内唯一のDirectX12に関する書籍です.なお,同人であればすらりんラボさんやノースブレインさんが出している書籍があります.

書評を書くにあたって,軽く自分の経歴を書いておくと,とあるゲームでリードプログラマを務めたり,ゲームエンジンに携わったりしています.

誤解しないで欲しいので最初に言っておきますが,私はこの本は良い本だと思っています.

誰のための書籍か

ゲームエンジンで独自のシェーダも扱える昨今,ゲームを作ったり表現の研究をしたりしたいのであれば,DirectX12を自分で扱う必要はありません.

ゲームエンジンを作る側になるにしても,描画周りの専門家になりたいというのでなければ,DirectX12を知る必要はありません.

描画のみを扱っているので,ゲームを作れるようになるかというと,なりません.ゲームを作れるようになるには,他にも乗り越える壁があります.

だったら読まなくても良いや,と思った人には向かない書籍です.

ぶっちゃけてしまうと,本書は多くの人には不要な書籍です.しかし,知識というのは,いつどこでどう役立つか分からないものです.自分が直接扱うわけではなくとも,描画周りの専門家と話をするときに役立つことだってあるでしょう.

また,手を動かす前提の書籍なので,キャラクタアニメーションだったり,ポストプロセスなどが,理論では良く分からないから,実際に実装して理解を深めたい,という方にはオススメです.

著者の川野先生は専門学校の先生で,学生が少しでも分かりやすいように,と色々と工夫されているようで,本書もなるべく理解しやすいように,ちょっとずつコードを追加していける形でサンプルが作られています.誰かに描画周りについて説明する場合の参考にもなるでしょう.

まとめると,すぐに役立つ知識が欲しい人には向きません.将来を見据え,何でも学ぶ姿勢の人にこそ合う本です.

この本の利点

最大の利点は,やはりサンプルです.理解を深めるために良い例題やサンプルを用意できるか,というのが専門書の重要なポイントだと思います.

同じ内容を扱った色々な専門書があるのも,説明の仕方や,例題を変えることで,少しでも理解が深まる人が増えれば,と考えてのことだと思います.

本書のサンプルは,徐々に機能を足していき,ある程度足し終えれば,ちょっと書き換えるだけで,次の例を実装できるようになっており,手を動かしていると楽しくなってきます.

キャラクタが表示できるとちょっと楽しくなり,それを増やせるとさらに楽しくなり,さらにアニメーションが付くと,さらに楽しくなります.

人によっては,ポストエフェクトなどの方が楽しいかもしれません.

あまりコードを分けすぎず,コードを把握するのも多少は楽な作りになっているので,細かく分かれすぎていて,クラスの関係が良く分からない,といったこともありません.

また,ImGUIやEffekseerといったミドルウェアとの連携についての情報はあまり無いので,それだけでもお金を出す価値はある気がします.

この本の欠点

利点を述べた以上,欠点も述べておくべきでしょう.

あくまでも公式ドキュメントなどを元に著者が調査した結果であるため,一部の用語が間違っていたり,勘違いを含んでいたりします.

また,論文などを参照しているわけでもないので,理論面で正確ではない部分や,著者も良く分かっていないと書いてある場合もあります.

ただ,それらは本書のターゲットがそういった厳密さを求めている人というわけではないので,気になるのであれば自分で調べれば良いでしょう.

また,コードの書き方についても若干癖があるので,そのあたりは,本書を読めば,GitHubのあちこちにあるDirectX12のサンプルコードを読み解く力が多少はついていることでしょうから,これが正解と思うのではなく,他のコードも色々と読むのが良いと思います.

手を動かす場合の注意点

まず,この書籍を読み進めながら手を動かすには,入門書レベルのC++は理解していて,外部ライブラリを利用する方法も知っているか,調べて解決できて,エラーが出た場合もある程度は自力で調べたりデバッガを利用して解決できる能力が必要です.

Visual Studio 2017 15.5以降,標準に準拠する,というオプションがデフォルトでオンになっており,サンプルコードの一部がビルドエラーになります.

自力でエラーを修正するか,プロジェクトのプロパティから,C/C++を選び,言語から準拠モードをいいえに切り替えましょう.

書籍の説明はところどころに抜けがあったりして,以前に設定したパラメータを切り替えておかないと上手く動かない部分があったりします.WinMergeなどのテキスト比較ツールを利用して,サンプルコードの前の章のコードと比較して差分を確認するなどしながら進めましょう.

また,9章ではリファクタリングと言いつつ,大幅な書き換えもあるので,その辺で一度復習しておくのも手です.なお,その関係で9章と8章の差分を取るのは困難というか不可能に近いです.

正誤表やIssue,Twitterなどを活用しよう

文章の間違いを見つけた場合,翔泳社の書籍紹介ページから正誤表に飛ぶことが出来るので,正誤表を確認してみましょう.載っていなければ,気軽に間違いを送ってみましょう.本当に間違いであれば,ちゃんと正誤表に載せてもらえます.

また,DirectX12のようにハードウェアに近い処理を扱う場合,利用しているハードウェアやドライバの影響で動かないことなどもあるでしょう.
そういうときは,サンプルコードがGitHubに上がっているので,気軽にIssueとして投げてみましょう.
著者の川野先生は忙しくて対応できないようですが,それを見た他の人が何かしらアクションしてくれるかもしれません.

また,Twitterなどで#DirectX12の魔導書 のタグを付けてつぶやくなどしていると,バグなどがあった場合に,著者であったり経験者の方がサポートしてくれたりします.

最後に

他のレビューでもあるように,この書籍には誤字脱字や説明の間違いなどが多々ありますが,世の中の専門書というものを見てみると,当然のように正誤表があり,色々と間違っていたり,版を重ねた際に誤植するなどもあるので,そのあたりは気にしない方が良いでしょう.
その点だけを以て,この書籍が良くないと断じているのであれば,世の中の多くの専門書が良くないことになります.

そもそも,最近はUnreal Engine 4やUnityなど,ゲームエンジンに関する書籍は多く出ているものの,こういった根元の部分を扱う書籍は長らく出ていませんでした.

勉強したくても,古い情報を元にWeb上で色々と調べて,断片的な情報をつなぎ合わせて,情報を更新していくしかありませんでした.

ちょうどそういった,根元の部分を扱うような書籍が出ると良いな,という話をしていた頃にこの書籍が出版され,情報や前例が少ない中で出版までこぎ着けてくださった川野先生や翔泳社の方には感謝しかありません.

この後に続く,XAudio2などのサウンドを扱った書籍や,DirectInputやXInputなど入力周りを扱った書籍など,自分で根元から作ってみたい人向けの書籍が増えることを期待します.

2020年6月25日木曜日

STLのtype_traitsを実装する その2

STLのtype_traitsを実装する その1の続き.

conjunctionを上手く実装できないか試行錯誤していて,たぶんこれでOKというのが出来たので書いておきます.

conditionalおよびconditional_tの実装

conditionalは,型のif文だと思えば良いでしょう.boolと型Tと型Fを渡すと,boolがtrueのときはtypeとしてTを,falseのときはtypeとしてFが手に入るわけです.

template <bool, class T, class F>
struct conditional
{
    using type = F;
};

template <class T, class F>
struct conditional<true, T, F>
{
    using type = T;
};

template <bool B, class T, class F>
using conditional_t = typename conditional<B, T, F>;

boolの値がtrueの場合を特殊化していますが,デフォルトはtypeがTになって,falseの場合はFになる,と逆にしても問題はありません.

conjunctionおよびconjunction_vの実装


conjunctionは,is_XXX系の型が特定の性質を持っているかを判定するtype traitsに対する論理積になります.通常の論理積と同じく,短絡評価が可能です.

つまり,is_integral_v<float> && is_floating_point_v<float>と書くと,最初のfloatが整数かどうかの判定でfalseになるので,全体としてfalseが決定するので,is_floating_point_v<float>は参照しない,というようなものです.

ただし,ここで問題なのが,&&でtype traitsの結果の論理積を取ってしまうと,参照はしないけれど,テンプレートの実体化は行われている,ということです.

例えば,has_valueというtype traitsがあって,与えられた型TがT::valueというような名前を持つかチェックする場合,has_value<int>が実体化されるとコンパイルエラーになるわけです.しかし,conjunctionを使うと,そういった問題は発生しません.

conjunction<is_floating_point<int>, int>;

最初の時点でfalseになるので,それ以降はテンプレートの実体化が行われず,2つ目のintはint::valueをチェックする,という処理が行われないわけです.

あとは,少しややこしいのが,親となるクラスを決定する方法です.

  • conjunction<>と実引数が0の場合,true_typeを継承.
  • conjunction<T0, T1, ..., Tn>の場合,Ti::valueがfalseなら,Tiを継承
  • conjunction<T0, T1, ..., Tn>の場合,全てのTi::valueがtrueなら,Tn(つまり最後の型)を継承

となるわけです.

というわけで,おそらく次のようになります.


// 実引数が0の場合は,ここで終わる
template <class... B>
struct conjunction : true_type {};

// 実引数が1の場合は,ここで終わる
template <class H>
struct conjunction<H> : conditional_t<static_cast<bool>(H::value), H, H>
{};

// それ以外
template <class H, class... T>
struct conjunction<H, T...> : conditional_t<static_cast<bool>(H::value), conjunction<T...>, H>
{};

template <class... B>
inline constexpr bool conjunction_v = conjunction<B...>::value;

最初の定義は,実引数が0の場合に適用されます.1つの場合,2つ目の定義で::valueを持っているかチェックした上でその型を利用します.
trueでもfalseでも,最後の1つならその型を使うことに変わりはないので,これで問題ないはずです.

3つ目の定義は,2つ以上ある場合で,trueなら残りをチェックし,falseならその型を継承するようにしています.

名前空間myに定義していたとして,次のようなテストをしています.

my::conjunction<my::is_integral<int>> c0;
my::is_integral<int> * pc0 = &c0;

my::conjunction<is_integral<int>, my::is_floating_point<int>> c1;
my::is_floating_point<int> * pc1 = &c1;

my::conjunction<is_integral<int>, my::is_floating_point<int>, int> c2;
my::is_floating_point<int> * pc2 = &c2;

// my::conjunction_v<int>; // コンパイルエラー(int::valueができないため)

static_assert(my::conjunction_v<>);
static_assert(my::conjunction_v<std::is_integral<int>>);
static_assert(my::conjunction_v<std::is_integral<int>, std::is_floating_point<float>>);
static_assert(!my::conjunction_v<std::is_integral<int>, std::is_floating_point<int>>);
// 短絡評価されるので,intを指定していても問題無し
static_assert(!my::conjunction_v<std::is_integral<float>, int>);

clangの実装でもMSVCの実装でも,condtionalを使っていなかったので,もしかしたら何か抜けがあるのかもしれませんが,今のところ上手く動作していそうです.

こういうパターンが網羅できないよ,というのがあれば指摘いただけると助かります.

2020年6月23日火曜日

STLのtype_traitsを実装する その1

C++の標準ライブラリにはtype_traitsという,型に対して様々な判定をしたり,変換をしたりするテンプレートが用意されています.こういうテンプレートをtype traitsと言います.

これを独自に実装するというのは車輪の再発明になりますが,例えばテンプレートで配列の場合のみ特殊化したい場合,どう書けば良いのか,などテンプレートの書き方について色々と勉強になります.

なお,ここではC++17以降を想定しています.

integral_constantの実装

まず,integral_constantを実装します.これは,type_traitsにある色々な判定系のテンプレートの土台になっています.また,boolの場合に限定したbool_constantエイリアステンプレートや,その値がtrueの場合のtrue_type,false_typeは,型が特定の条件を満たすか判定する際の土台として使います.

template <class T, T v>
struct integral_constant {
    static constexpr T value = v;
    using value_type = T;
    using type = integral_constant<T, v>
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; }
};
template <bool B>
using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;

is_sameおよびis_same_vの実装


次に,2つのテンプレートパラメータの型が同じ場合にtrue,違う場合にfalseとなるvalueを持ったis_sameという型を実装します.また,通常はis_same<X, Y>::valueと書いて値を読み取るところを,C++14以降では,直接読み取れるようにした
is_same_vも追加されているので,そちらも実装します.

template <class T, class U>
struct is_same : false_type {};

template <class T>
struct is_same : true_type {};

template <class T, class U>
inline constexpr bool is_same_v = is_same<T, >::value;

このように,通常はfalse_typeを継承していて,型が同じ場合を特殊化してtrue_typeを継承するようにすることで,valueの値は型が同じときだけtrueになります.

これを実装しておくと,次に実装する,型を変形するtype traitsのテストがしやすくなります.is_sameも,次のようにstatic_assertを利用してテストしておきましょう.

static_assert(is_same_v<int, int>);
static_assert(!is_same_v<int, const int>);

本当は,もっと色々なパターンをテストしておいた方が良いでしょうが,その辺は実装する際に色々考えてみると良いでしょう.

remove/add系の実装


次に,型を変形するtype traitsを実装します.何故かというと,その型が何なのかを判定するis_XXX系のtype_traitsではconstなどは無視するケースも多々あり,そこでこれから実装するremove_constなどを利用するからです.

これらの型を変形するtype traitsには,typeという名前で変形後の型にアクセスできます.

まずは,constを取り除くremove_constを実装しましょう.また,typeに直接アクセスできるremove_const_tも実装します.

template <class T>
struct remove_const
{
    using type = T;
};

template <class T>
struct remove_const
{
    using type = T;
};

template <class T>
using remove_const_t = typename remove_const<T>::type;

エイリアステンプレートでtypeと取り出す際には,typenameが必要です.

逆に,constを付けるadd_constの実装はシンプルです.

template <class T>
struct add_const
{
    using type = const T;
};

template <class T>
using add_const_t = typename add_const<T>::type;

constで無い型にはconstが付き,既にconstな型なら追加のconstは無視されるんですね.

では,テストを書いておきましょう.

static_assert(is_same_v<remove_const<int>, int>);
static_assert(is_same_v<remove_const<const int>, int>);
static_assert(is_same_v<remove_const<const int *>, const int *>);
static_assert(is_same_v<remove_const<const int * const>, const int *>);

本当は,もう少し色々書くべきですが,省略します.ここで注目すべきは,ポインタの場合,constはポインタに対して付いているものが取り除かれる,ということです.
const int *がint *になると思った方もいるかもしれませんが,そうはなりません.

こんな感じで,独自のtype_traitsを実装していきます.

remove_constとadd_constのconstをvolatileに置き換えるだけで,remove_volatileとadd_volatileが作れます.

この2つを組み合わせれば,remove_cvやadd_cvが作れます.

その2は……気が向いたら書きます.

2020年5月20日水曜日

C++ Templates - The Complete Guide, 2nd 読書メモ 2章 クラステンプレート

C++ Templates - The Complete Guide, 2ndの2章「クラステンプレート」の読書メモ

スタックの実装を例にクラステンプレートについて解説している.


クラステンプレートの宣言


関数テンプレートと同様に,templateの後にテンプレート仮引数を並べ,クラス宣言を書く.

template <typename T>
class Stack {
  ...
};

クラステンプレート内では,Tを型として利用できる.

コピーコンストラクタなどを書く場合,次の2通りの書き方があるが,敢えて書くと意味があるように思わせるので,前者の方が良い.

Stack(const Stack &);
Stack(const Stack<T> &);

メンバ関数の実装


クラステンプレートのメンバ関数を実装する方法として,クラスの外側で定義する方法とクラス内で定義する方法が紹介されている.

ついでに,例外安全についても触れていて,Exceptional C++が紹介されている.

クラステンプレートの利用


基本的には,クラステンプレートを利用する場合は型を明示する必要があるが,C++17では型を省略する方法が追加され,後ほど解説する,とある.

また,呼び出されたテンプレート(メンバ)関数のみがインスタンス化される,とある.

friend


フレンド関数を定義するには,クラス内で宣言してしまう方法と,後から別途宣言する方法が2つ紹介されている.

template <typename U>
friend std::ostream & operator<<(std::ostream &, const Stack<U> &); // 別途非メンバ関数として定義する

template <typename T>
class Stack; // 前方宣言
template <typename T>
std::ostream & operator<<(std::ostream &, const Stack<T> &);

template <typename T>
class Stack {
    friend std::ostream & operator<< <T>(std::ostream &, const Stack<T> &);
};

テンプレートの特殊化


template <>として,クラス名の後に具体的な型を指定して定義することで,特定の型の場合について専用の実装を用意することができる.

例としては,std::vector<bool>など.

例えば,typename Tに対して,T *,とポインタ型にするような部分的な特殊化も可能.

デフォルト実引数


関数テンプレートと同様に,テンプレート仮引数にデフォルト実引数を指定することが可能.

タイプエイリアス


typedefのように型の別名を定義する方法として,usingによるエイリアス宣言が紹介されている.

typedefよりも = でつなぐ分,読みやすい,とも.

また,エイリアスはテンプレートにもできる.

template <typename T>
using DequeStack = Stack<T, std::deque<T>>;

C++14以降では,標準ライブラリでは 型とレイトである型から他の型を取り出す場合,今まではA::typeとしていたのを,A_tというようなエイリアスが用意された.

クラステンプレート実引数推論


C++17から,初期化時の実引数からクラステンプレートの型を推論できるようになった.使い方次第では便利なのだろうけれど,使いこなせない気がする.

文字列を利用して推論する場合の注意点についても触れている.コンストラクタが参照を受け取るようになっていると,文字列から推論しようとするとconst char [x]のような型になってしまうが,値渡しにしていると,const char *になる.

推論ガイド(deduction guide)についてもサラッと触れている.

テンプレート化された集成体


ユーザー定義または継承したコンストラクタを持たず,privateやprotectedな非staticメンバを持たず,仮想関数,仮想またはprivateまたはprotectedなベースクラスを持たないクラスまたは構造体をテンプレートにすることもできる.

2020年5月7日木曜日

C++ Templates - The Complete Guide, 2nd 読書メモ 1章 関数テンプレート

C++ Templates - The Complete Guide, 2ndの1章「関数テンプレート」の読書メモ

関数テンプレートとは

int max(int, int);
double max(double, double);
string max(string, string);

このように,型が違うだけで同じような関数をまとめて,次のように型名をtypenameで仮引数として定義できる.

template <typename T>
T max(T, T);

family of functions,という表現がポイントなのかな,と.

この関数テンプレートは,次のように呼び出せる.

max(1, 3);
max(1.0, 3.0);

このとき,具体的にTにintやdoulbeがあてはめられて関数が生成される.具体的な関数が生成されることをインスタンス化(instantiation)と呼び,生成された関数をテンプレートのインスタンス(instance)と呼ぶ.

Two-Phase Translation

テンプレートは,定義をしたタイミングと,具体的な型が割り当てられたタイミングの2つのタイミングで解釈が行われる.例えば,上記のmaxであれば大小比較が必要なので,大小比較が定義されていない型が渡されると,インスタンス化のタイミングでエラーとなる.

一方で,テンプレートの定義時点で定義されていない型名が関数テンプレート内で利用されている,といった文法上のエラーなどがある場合に,仕様上はエラー判定が行われるのだけれど,Microsoft Visual C++はその辺が緩い.Clangだとちゃんとエラーになる.その辺の挙動を切り替える方法については,次のリンク先にまとめてある.


型推論時の型変換

関数テンプレートは,呼び出し時に渡された実引数から型を自動的に決定する.これを型推論(Type Deduction)と言う.

この型推論が行われる際には,型変換が制限される.例えば,doubleを受け取る関数にintの値を渡しても,普段であれば暗黙の型変換が行われるが,関数テンプレートの場合,エラーになる.
max(1, 3.0); // コンパイルエラー

これを回避するには,明示的にキャストをするか,関数テンプレートの型を直接していする.

max(1, 3.0); // T=doubleとして判定される.

ちなみに,必要性が分からないのだけれど,デフォルト実引数を指定していたとしても,デフォルト実引数の型をもとに型推論は行われない,とのこと.

複数のテンプレート仮引数


標準ライブラリのmaxはそうなっていないけれど,説明のためにmaxのテンプレート仮引数を複数にして,複数の型を受け取れるようにしよう,という説明が進む.

こうすると,今度は返り値の型はどちらにそろえるんだ,といった問題が発生する.

そこで,返り値の型を推論する方法が示される.(書籍よりコードを抜粋)

template <typename T1, typename T2>
auto max(T1 a, T2 b) -> std::decay_t<decltype(true ? a : b)>
{
    return b < a ? a : b;
}

返り値の型を指定するところにautoを書いて,後ろにアロー(->)を書いて,decltypeで式の結果の型を求める.結果が参照などでは困る場合があるので,std::decay_tを利用して返り値の型に適切な型に変換する.

次のように,std::common_type_tを利用する方法も示されている.

template <typename T1, typename T2>
std::common_type_t max(T1 a, T2 b)
{
    return b < a ? a : b;
}

あくまでも説明のために出しているだけで,実際にこれをやると,intとdouble,doubleとint,のように型がひっくり返っているだけでも別の関数が生成されてしまい,プログラムサイズが無駄に増えるので望ましくないと思われる.

ちなみに,a > bとしないのは,C++の仕様を見るに,基本的には < を基準に比較演算が考えられているからだと思われる.

デフォルトテンプレート実引数


通常の関数と同様に,テンプレート仮引数にもデフォルト実引数を指定することができる.ただし,関数テンプレートにデフォルトテンプレート実引数を指定できるようになったのは,C++11以降なので注意が……今時必要ない気がするけど,未だにC++11さえも使えない環境もあるかもしれない.そうだとすると相当ツライ……

関数テンプレートのオーバーロード


通常の関数と同様に,関数テンプレートもオーバーロードできる.これ,意外と驚かれる.通常の関数もオーバーロードに含めた場合,どちらが優先されるかなどについては,13章で詳しく扱うようだ.第一版では9章だったので,4章ほど追加されているのかな.

その他


この単純なmax関数だけでも,色々考えることがあって,値渡しと参照渡し,どちらが良いのか,inlineにはできないのか,constexprは,など色々考えることがあるよ,と.

2020年5月6日水曜日

MSVCとClangにおけるテンプレートの名前の解釈の切り替え

Clangのドキュメントによると,Microsoft Visual C++では,テンプレートがインスタンス化されるまでインラインメソッドの本体の解析が行われないが,clangは行う.これだと,Windows系のヘッダファイルの解析時に問題になるので,-fdelayed-template-parsing,というオプションを指定することで,VC++と同じ挙動に出来る.

つまり,次のようなコードのコンパイルが通るようになってしまう.

template  void f(Type t) {
    Typo t; // 敢えてタイプミス
}

int main()
{
    // fのインスタンス化はしていない
}


一方で,VC++をClangと同じようにする方法がある.プロジェクト設定のC/C++の言語から,準拠モードを「はい(/permissive-)」にすれば良い.

詳細についてはC++ Team Blogで紹介されている.

MS Docsによれば,Visual Studio 2017 15.5以降ではデフォルトでこの設定が「はい」になるとのこと.逆に言えば,それより前から継続して使っているプロジェクトには設定されていないということなので,何かのきっかけで新しいプロジェクトを追加した際などには,この設定の差が問題になるかもしれない.