下記の記事に関する補足ですが、量が多いので別記事にします。
泥縄的学習法...
array の初期化でも2重 {} が必要だった(?)
cpprefjpによると C++11 では2重の {} が必要だったようです。
std::array<double,3> x1 = {1, 2, 3}; // こう書けるのは C++14 から
std::array<double,3> x2 = {{1, 2, 3}} // C++11 ではこう
と思ってこの項目を作ったのですが、よく調べてみるとちょっと違いそう。
「C++11時点では2重 {} が必要だった場合がある」が正しそうです。
多分こういうこと。
std::array<double,3> x1 = {1, 2, 3}; // C++11 でもOK
std::array<double,3> x2 = {{1, 2, 3}} // C++11 でももちろんOK
std::array<double,3> x3{1, 2, 3}; // こう書けるのは C++14 から
std::array<double,3> x4{{1, 2, 3}}; // C++11 でももちろんOK
Issue #1270 というのが参照されていますが、この1270 は「= がある時だけ省略できる」のがバグと言っているのだと思われます。
まあ今さらどちらでもいいけど...
参考リンク:
- initializer-listによるaggregate初期化の制約緩和
- vector/arrayとUniform initialization+Initializer list
- C++ Standard Core Language Defect Reports and Accepted Issues, Revision 113
(C の) 構造体の初期化における未指定要素の値
以下のページより引用:
そして、初期化リストによって初期化する際に、配列や構造体のメンバーに、対応する初期化リストの値がない場合、staticストレージと同じように初期化される。staticストレージは、必ずゼロで初期化されることが保証されている。したがって、上記のコードは、「a[0]を0で初期化し、残りの要素をstaticストレージと同じ方法で初期化せよ」という意味である。
この記事によると私の「C 言語では末尾に 0 を書くと後続の未指定要素は 0 になる」という認識が間違い。単に「未指定要素が 0 になる」。 で C++ の集成体の初期化でも同じ動作が継承されていると。
ちなみに上記記事では「初期化リスト」と「std::initializer_list」を別物として使い分けて記事が書かれているようです。この記事における「初期化リスト」の詳細を知りたい...
そもそも array を直接座標値型として使えばいいんじゃね?
soreja さんより 以下のようにすればいいのでは、とのコメントをいただきました。 この件について所感を。
namespace cpp14 {
using Vector3d = std::array<double, 3>;
template<class T, size_t N>
std::array<T, N>& operator+=(std::array<T, N>& l, const std::array<T, N>& r);
}
array を直接座標値として使う方針について
元記事を書きながら「array を直接利用すればいいんじゃね」という発想は得ていましたが、どちらかというと否定的でした。
気になる点
- 演算子の自前オーバーロードを std 名前空間に定義するのに抵抗があった。(クラスと同じ名前空間(std)に入れるべしと思い込んでいました。)
- メンバ関数の追加ができない。(C# なら拡張メソッドとかで対応する部分。)
- 長さ/ノルム の計算をするメンバ関数とか。 2項演算系のものはグローバル関数として実装するのでもよいと思いますが、そうじゃないものもあるかなと。
- array のデータメンバが public。
一方で、 C 言語で double の配列を座標値として使う方針がありなら、それ以上には array を座標値として使うのはありだと思います。 メンバ関数の形で実装しなくてはならないのは仮想関数位で、座標値クラスで仮想関数はほぼ不要でしょう。 あとはメンバ関数とグローバル関数の違いは見た目・趣味の問題でしかないと思います。 (アクセス制限の話はなくはない。また趣味の問題と言いつつ、今さら関数ライブラリを使いたいかと言われれば抵抗も感じますが。)
今回は自分の中で「既に存在する VectorN に {1, 2, 3}
みたいな表記を受け入れさせたい」という暗黙の前提があったので状況が異なりますが、例えば計算用には 2D版, 3D版を個別に実装し、n 次元が欲しい時だけ array を使うという方針はありだと思います。
計算用と array 版でインタフェースが揃わないという指摘は以下の観点で大きな問題にはならないでしょう。
- そもそも n 次元版共通のインタフェースはそれほど多くない。 むしろベクトルの外積のように次元毎にインタフェースが違うものもある。
- 「N次元版が欲しい」の中身は、実際上割と array で事足りる。
- クラスの違いは Traits クラスで吸収することも可能。
std::array の演算子はオーバーロードしないかも
他方 soreja さんのサンプルにある演算子のオーバーロードですが、あれは私はやらないか、局所的なコードとするかなあと思います。 マナーとしては、演算子のオーバーロードはクラスと同じ名前空間で定義されるべきと思っているので。