C ++构造函数初始化列表调用默认构造函数。为什么?

时间:2014-02-26 11:58:30

标签: c++ constructor initialization-list

看看我的代码。它会将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

2 个答案:

答案 0 :(得分:4)

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


1 虽然我有一个理论:它可能会尝试初始化rgba

答案 1 :(得分: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的复制构造函数,因为它的参数声明为非const引用,而临时对象可能不会绑定到非const引用。

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

因此构造函数会搜索其他路径来执行该任务。

它可以将临时对象转换为float

类型的对象
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