C++ C++;混淆cast运算符和可变构造函数

C++ C++;混淆cast运算符和可变构造函数,c++,c++11,variadic-templates,typecast-operator,C++,C++11,Variadic Templates,Typecast Operator,C++(更具体地说,MinGW对g++的实现)越来越混乱。我有一个数学向量类,它包含任意数量的任意类型的元素。元素类型和元素数量在编译时指定 Vector类混淆了它的一个构造函数和我称之为“resize”操作符的东西。resize操作符允许程序员将一个大小的向量转换为另一个任意大小的向量。如果强制转换向量的元素数比基向量的元素数多,则会使用1填充。以下是实现: /* * resize operator: * T is the type of element the base vector h

C++(更具体地说,MinGW对g++的实现)越来越混乱。我有一个数学向量类,它包含任意数量的任意类型的元素。元素类型和元素数量在编译时指定

Vector类混淆了它的一个构造函数和我称之为“resize”操作符的东西。resize操作符允许程序员将一个大小的向量转换为另一个任意大小的向量。如果强制转换向量的元素数比基向量的元素数多,则会使用1填充。以下是实现:

/*
 * resize operator:
 * T is the type of element the base vector holds
 * N is the number of elements the base vector holds
 * rN is the size of the new vector
 */
template<typename T, unsigned int N, unsigned int rN>
operator Vector<T, rN>() const 
{
    Vector<T, rN> resize;

    for (unsigned int i = 0; i < rN; i++)
    {
        resize[i] = i < N ? this->elements[i] : 1;
    }

    return resize;
}
但不是这个

vec3 bar(vec4(1, 2, 3, 4), 5);
我通过使用计数器递归所有元素,确保在编译时提供了适当数量的元素,然后使用静态断言确保计数器以向量可以包含的元素数结束。除以下代码外,这通常效果良好:

vec4 bar(1, 2, 3, 4);
(vec3) bar; //PROBLEM HERE
<>发生的是,C++认为(VEC3)条是在请求变量构造时,实际上它应该调用Resig操作符。我试着让他们明白,但没用。如何确保C++在使用上述代码时使用RealSmithTo操作符而不是变量构造函数?< /P> 简而言之,我如何告诉C++使用这个:

//resize operator
template<typename T, unsigned int N, unsigned int rN>
Vector<T, N>::operator Vector<T, rN>();
如果不清楚,vec3和vec4的定义如下:

typedef Vector<float, 3> vec3;
typedef Vector<float, 4> vec4;
但这段代码仍然给了我一个静态断言失败:

vec3 foo = static_cast<vec3>(someVec4);
vec3 foo=static_cast(someVec4);
这基本上没有意义,因为我声明了变量构造函数显式,因此不应该在那里调用它

另外,根据要求,这里有一个


TL;DR版本是,当我尝试显式调用typecasting运算符时,我的代码正在调用显式构造函数,但当我尝试隐式调用它时,代码不会调用显式构造函数。

使您的构造函数显式并使用:

vec4 someVec4; 
// ....
vec3 someVec3 = someVec4;

没有混乱。构造函数总是优先于转换函数,在您的情况下,您的类型总是可以从任何类型的参数进行构造。下面是一个大大简化的示例:

struct foo {
    template<typename T>
    foo(T t);
}

template<typename T>
foo::foo(T)
{ static_assert( std::is_same<T, int>::value, "" ); }
这将是先前设计的示例的SFINAE版本。有了这样一个声明,那么类似于
foo f=42不会产生与以前相同的错误。编译器会抱怨,例如,没有从
double
foo
的适当转换,就好像构造函数根本不存在一样。这就是我们想要的,因为如果不存在这样的构造函数,那么规则规定将搜索适当的转换运算符。(好吧,这并不是说这对双倍的情况有很大帮助,但无论如何。)


请注意,有几种方法可以使用SFINAE,而这一种恰好是我最喜欢的形式。通过了解SFINAE,您可以找到其他人。(顺便说一句,正确使用模板别名并没有那么可怕,它最终看起来像
EnableIf…

我认为最简单的方法是让代码正常工作,就是用转换构造函数替换转换操作符。由于该构造函数比可变构造函数更专业,因此它应该始终优先。

查看您的,您可以应用一些清理步骤

通用构造函数模板的最大问题是它匹配所有内容,除非非模板构造函数完全匹配。如果你连简历资格都错了,你会选择通用的构造器模板。当我遇到类似问题时,建议我添加一个标记值作为第一个参数:

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 );
将数组作为函数参数时,默认情况下它将被视为指针,忽略任何大小信息。如果需要保持大小,则必须通过引用传递:

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.
模板
类向量
{
//...
向量(T常量(&set)[N]);/“T集合[N]”->“T*集合”
//...
};
//...
int aa[4]={1,2,3,4},bb[3]={5,6,7};
向量vd(aa);//新签名不接受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;
}
#包括
#包括
#包括
枚举标记{mark};
模板
类向量
{
//“设置”功能是不必要的,见下文。
公众:
//自动定义的复制中心、移动中心、复制分配和
//移动和分配都可以。
T元素[N];
向量():元素{}{}
//向量():向量(T{}{}//ALTERNATE
//如果以下构造函数使用默认参数,则可以删除。
向量(T为空)
//向量(T empty=T{})//ALTERNATE
{std::fill(元素,元素+N,空);}
向量(T常量和集合)[N])
{std::copy(set,set+N,elements);}
向量(std::初始值设定项\u列表集)
:元素{}
{
断言(set.size())
显式向量(向量常数&v)
:矢量(静态_投射(1))
{std::copy(v.elements,v.elements+std::min(R,N,elements);}
T运算符[](无符号整型参数)(&T)
{返回此->元素[param];}
常量T&运算符[](无符号int参数)常量
{返回此->元素[param];}
};
typedef向量vec2;
typedef向量vec3;
typedef向量vec4;
int main()
{
vec4-someVec4(标记1,2,3,4);
vec3 foo=静态施法(someVec4);
返回0;
}

“代码如何”?“代码如何”?“我如何告诉C++使用…”?我不确定你在问什么。我在调整大小运算符之前定义和实现构造函数,如果你是这个意思。请看“总之,我如何告诉C++使用这个:”在你的问题中。
struct foo {
    template<typename T>
    foo(T t);
}

template<typename T>
foo::foo(T)
{ static_assert( std::is_same<T, int>::value, "" ); }
struct foo {
    template<
        typename T
        , typename std::enable_if<
            std::is_same<T, int>::value
            , int
        >::type...
     >
     foo(T t);
};
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 );
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;
}