0

看看我的代码。它将 Vector4 转换为 Vector4。有这个复制构造函数,它会自动逐个组件地进行转换。我无法理解一件事:为什么在构造结束时,正确设置每个组件时,会在每个组件上调用默认构造函数,从而使输出向量为空。您可以在下面的输出中看到执行流程。有趣的是,如果我用 4 个赋值替换初始化列表,代码会按预期工作。

编译器是VS2013。

#include <cstdio>

using namespace std;

struct half
{
    unsigned short data;

    half() : data(0) { printf("half::default_constructor\n"); }
    half(half& pattern) : data(pattern.data) { printf("half::copy_constructor\n"); }
    explicit half(float pattern) : data(16) { printf("half::from_float_constructor\n"); }
    operator float() { printf("half::to_float_operator\n"); return 3.0f; }
};

template <typename T>
struct Vector4
{
    Vector4() : x(0), y(0), z(0), w(0) { }
    Vector4(T value) : x(value), y(value), z(value), w(value) { }
    Vector4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) { }

    template <typename U>
    Vector4(Vector4<U>& other) : x((T)other.x), y((T)other.y), z((T)other.z), w((T)other.w) { }

    union
    {
        struct { T x, y, z, w; };
        struct { T r, g, b, a; };
    };
};

int main()
{
    Vector4<float> a(0, 1, 4, 6);
    Vector4<half> b(a);
}

该程序的输出:

half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::from_float_constructor
half::to_float_operator
half::from_float_constructor
half::default_constructor
half::default_constructor
half::default_constructor
half::default_constructor
4

2 回答 2

4

原因是代码无效。您不能在联合中存储非 POD 类型。您的代码会导致未定义的行为。我不知道编译器究竟做了什么,以及为什么它调用默认构造函数1—— 但这对你来说是未定义的行为。


1虽然我有一个理论:它可能会尝试初始化rg和。ba

于 2014-02-26T12:00:06.390 回答
2

首先,C++ 没有匿名结构。所以这个 Vector4 成员的定义

union
{
    struct { T x, y, z, w; };
    struct { T r, g, b, a; };
};

不符合 C++ 标准。我认为您使用具有这种语言扩展名的 MS VC++

现在让我们考虑会发生什么。

在模板构造函数的 mem-initializer 列表中

 template <typename U>
    Vector4(Vector4<U>& other) : x((T)other.x), y((T)other.y), z((T)other.z), w((T)other.w) { }

例如,C 样式转换(T)other.x调用类的构造函数half

explicit half(float pattern) : data(16) { printf("half::from_float_constructor\n"); 

此调用的结果是创建一个临时对象类型half

您可能不会应用类的复制构造函数,half因为它的参数被声明为非常量引用,并且临时对象可能不会绑定到非常量引用。

half(half& pattern) : data(pattern.data) { printf("half::copy_constructor\n"); }

所以构造函数搜索其他路径来完成任务。

它可以将临时对象转换为浮点类型的对象

operator float() { printf("half::to_float_operator\n"); return 3.0f; }
};

最后调用构造函数

explicit half(float pattern) : data(16) { printf("half::from_float_constructor\n"); }

因此,您会得到以下消息序列

half::from_float_constructor
half::to_float_operator
half::from_float_constructor

我准备了一个更简单的 C++ 兼容示例来演示相同的行为

#include <iostream>

struct A
{
    float x = 0.0f;
};

struct B
{
    explicit B( float ){ std::cout << "B::from_float_constructor" << std::endl; }
    B( B & ){ std::cout << "B::from_copy_constructor" << std::endl; }
    operator float () const 
    { 
        std::cout << "B::to_float_operator" << std::endl; 
        return 0.0f;
    }
};

struct C
{
    B b;
    C( A a ) : b( ( B )a.x ) {}
};

int main() 
{
    A a;
    C c( a );

    return 0;
}

输出是

B::from_float_constructor
B::to_float_operator
B::from_float_constructor
于 2014-02-26T12:36:32.733 回答