数学的ベクトルに基づいたクラスを作成しようとしています:
template <unsigned N> class Vector{
public:
Vector() = default;
Vector(std::initializer_list<double> li) { *this = li;}
Vector& operator=(std::initializer_list<double>);
private:
std::array<double, N> x = {}
}
template <unsigned N> inline Vector<N>& Vector<N>::operator=(std::initializer_list<double> li){
if(N != li.size()) throw std::length_error("Attempt to initialise Vector with an initializer_list of different size.");
std::copy(li.begin(), li.end(), x.begin());
return *this;
}
このようなコードを書けるようになりたいです。
Vector<3> a = {1,2,3};
a = {3,5,1};
ユーザーがそのようなコードを書くことを期待するのは自然なことですよね? ただし、間違ったサイズの初期化子リストを使用すると、コンパイル時のエラーが発生するようにしたいと考えstd::array
ています。
std::array<double, 3> a = {2,4,2,4} //compile time error
Vector<3> a = {3,5,1,5} //run-time error as of right now
私の最初のアイデアはstd::array
、コンストラクター/オペレーターのパラメーターとして使用することでした。これにより、暗黙的な変換が発生し、コンストラクターがstd::array
コンパイル時エラーからハイジャックします。もちろん、次のようなコードしか書けませんでした。
Vector<3> a({2,3,2}); //fine
Vector<3> b = {2,4,2}; //error, requires two user-defined conversions (list -> array<double,3> -> Vector<3>)
Variadicメンバーテンプレートを使用することを考えました:
template <typename... Args> Vector(Args... li): x({li...}){
static_assert(sizeof...(li) == N);
}
非型パラメーターは整数型でなければならないため、そうする必要はありませんtypename...
。double...
しかし、その後、縮小変換エラーに遭遇します
Vector<2> a = {3,2} //error: narrowing conversion of 'li#0' from 'int' to 'double' inside { } [-Wnarrowing]|
//error: narrowing conversion of 'li#1' from 'int' to 'double' inside { } [-Wnarrowing]|
おそらく [8.5.4]/7 に違反するため
縮小変換は暗黙の変換です
— ソースが定数式であり、変換後の実際の値がターゲットの型に適合し、元の型に変換されたときに元の値が生成される場合を除き、整数型またはスコープのない列挙型から浮動小数点型へ。また
拡張からのパラメーターli...
は定数式ではないため、縮小変換エラーが発生します。私が知る限り、関数のパラメータを定数式にすることさえできません (あまり意味がありませんか?)。ですから、そのルートをどのように進めればよいかわかりません。明らかにVector<2> a = {2.,3.}
問題なく動作しますが、浮動小数点リテラルを提供することだけを覚えておくという負担がユーザーにかかります。