看看我的代码。它将把Vector4转换为Vector4。有一个复制构造函数,它自动逐个组件进行转换。我不明白一件事:为什么在构建结束时,当每个组件都正确设置时,会在每个组件上调用默认构造函数,使输出向量为空。您可以在下面的输出中看到执行流程。有趣的是,如果我用4个赋值替换初始化列表,代码就会按预期工作。
编译器是VS2013。
#include <cstdio>
using namespace std;
struct half
{
unsigned short data;
half() : data(0) { printf("half::default_constructorn"); }
half(half& pattern) : data(pattern.data) { printf("half::copy_constructorn"); }
explicit half(float pattern) : data(16) { printf("half::from_float_constructorn"); }
operator float() { printf("half::to_float_operatorn"); 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
原因是代码无效。不能将非POD类型存储在联合中。您的代码会导致未定义的行为。我不知道编译器到底做了什么,也不知道它为什么调用默认构造函数1——但这对你来说是未定义的行为。
1尽管我有一个理论:它可能试图初始化r
、g
、b
和a
。
首先,C++没有匿名结构。因此,Vector4成员的定义
union
{
struct { T x, y, z, w; };
struct { T r, g, b, a; };
};
不符合C++。我认为您使用的是具有此类语言扩展的MS VC++。
现在让我们考虑一下发生了什么。
在模板构造函数的mem初始值设定项列表中
template <typename U>
Vector4(Vector4<U>& other) : x((T)other.x), y((T)other.y), z((T)other.z), w((T)other.w) { }
以(T)other.x
为例的C样式转换调用类half
的构造函数
explicit half(float pattern) : data(16) { printf("half::from_float_constructorn");
此调用的结果是创建一个类型为half
的临时对象
不能应用类half
的复制构造函数,因为它的参数被声明为非常量引用,并且临时对象不能绑定到非常量引用。
half(half& pattern) : data(pattern.data) { printf("half::copy_constructorn"); }
因此构造函数会搜索其他路径来完成任务。
它可以将临时对象转换为float 类型的对象
operator float() { printf("half::to_float_operatorn"); return 3.0f; }
};
最后调用构造函数
explicit half(float pattern) : data(16) { printf("half::from_float_constructorn"); }
因此,您可以得到以下消息序列
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