查看您的SSCCE,您可以应用一些清理步骤。
通用构造函数模板的大问题是它匹配所有内容,除非非模板构造函数完全匹配。如果你连 cv 限定都错了,通用构造函数模板将被选中。当我遇到类似问题时,建议我添加一个标记值作为第一个参数:
enum my_marker { mark };
//...
template<typename T, unsigned int N>
class Vector
{
//...
template<typename ... Args>
explicit Vector(my_marker, Args ... args);
};
//...
Vector<int, 4> va( mark, a1, a2 );
您的其他构造函数不会使用此标记,因此现在您可以区分它们。顺便说一句,您与可以T
取值的构造函数有另一个重叠:
template<typename T, unsigned int N>
class Vector
{
//...
Vector( T empty );
Vector( std::initializer_list<T> set );
//...
};
//...
Vector<int, 4> vb{ 5 }; // always chooses the list ctr
Vector<int, 4> vc( 6 ); // I think this uses the single-entry ctr.
当您将数组作为函数参数时,默认情况下它将被视为指针,忽略任何大小信息。如果需要保持大小,则必须通过引用传递:
template<typename T, unsigned int N>
class Vector
{
//...
Vector( T const (&set)[N] ); // "T set[N]" -> "T *set"
//...
};
//...
int aa[ 4 ] = { 1, 2, 3, 4 }, bb[ 3 ] = { 5, 6, 7 };
Vector<int, 4> vd( aa ); // The new signature won't accept bb.
这种数组到指针的转换可以防止数组直接赋值,但在计算特殊函数时它们是隐式可赋值的。这意味着不需要您的赋值运算符;默认代码将做正确的事情。
你听说过迭代器吗?如果是这样,那么使用这些加上委托构造函数、标准算法和初始化程序可以减少您的代码。
#include <algorithm>
#include <cassert>
#include <initializer_list>
enum mark_t { mark };
template< typename T, unsigned N >
class Vector
{
// The "set" functions are unnecessary, see below.
public:
// The automatically defined copy-ctr, move-ctr, copy-assign, and
// move-assign are OK.
T elements[N];
Vector() : elements{} {}
// Vector() : Vector( T{} ) {} // ALTERNATE
// Can be removed if following constructor uses a default argument.
Vector(T empty)
// Vector(T empty = T{}) // ALTERNATE
{ std::fill( elements, elements + N, empty ); }
Vector(T const (&set)[N])
{ std::copy( set, set + N, elements ); }
Vector(std::initializer_list<T> set)
: elements{}
{
assert( set.size() <= N );
std::copy( set.begin(), set.end(), elements );
// If you were willing to use std::for_each, why not use a more
// appropriate algorithm directly? The lambda was overkill.
// WARNING: there's an inconsistency here compared to the cross-
// version constructor. That one fills unused spots with ones,
// while this one does it with zeros.
// WARNING: there's an inconsistency here compared to the single-
// value constructor. That one fills all elements with the same
// value, while this one uses that value for the first element but
// fills the remaining elements with zeros.
}
template<typename ... Args>
explicit Vector( mark_t, Args ... args)
: elements{ args... }
//: elements{ static_cast<T>(args)... } // ALTERNATE
{}
// Array members can now be directly initialized in the member part
// of a constructor. They can be defaulted or have each element
// specified. The latter makes the private "set" methods unnecessary.
// The compiler will automatically issue errors if there are too
// many elements for the array, or if at least one "Args" can't be
// implicitly converted to "T", or if you have less than "N" elements
// but "T" doesn't support default-initialization. On my system, the
// example "main" flags int-to-float conversions as narrowing and post
// warnings; the alternate code using "static_cast" avoids this.
template < unsigned R >
explicit Vector( Vector<T, R> const &v )
: Vector( static_cast<T>(1) )
{ std::copy( v.elements, v.elements + std::min(R, N), elements ); }
T &operator [](unsigned int param)
{ return this->elements[param]; }
const T &operator [](unsigned int param) const
{ return this->element[param]; }
};
typedef Vector<float, 2> vec2;
typedef Vector<float, 3> vec3;
typedef Vector<float, 4> vec4;
int main()
{
vec4 someVec4(mark, 1, 2, 3, 4);
vec3 foo = static_cast<vec3>(someVec4);
return 0;
}