Visual Studio 2013 上的 C++11 列表初始化怪异行为

C++11 list initialization weird behaviour on Visual Studio 2013

我正在尝试将一个数组传递给一个函数并防止每次都写入 "std::unique_ptr" 并使内联构造成为可能,我引入了一个 typedef (ItemList) 来为数组添加别名。

#include <iostream>
#include <memory>

class Base {
public:
    Base()
    {
        std::cout << "Base ctor" << std::endl;
    };

    virtual ~Base()
    {
        std::cout << "Base dtor" << std::endl;
    };
};

typedef std::unique_ptr<Base> ItemList[];

template<typename T>
class Derived : public Base {
    T val;
public:
    Derived(T i)
    {
        val = i;
        std::cout << "Derived ctor" << val << std::endl;
    };

    ~Derived()
    {
        std::cout << "Derived dtor" << val << std::endl;
    };
};

void dummyFunc(ItemList)
{

}

void testFunc()
{
    dummyFunc(ItemList{
        std::make_unique<Derived<int>>(2),
        std::make_unique<Derived<float>>(3.0f)
    });
}

//Entry point
int main()
{
    testFunc();
    return 0;
}

这在调试构建和打印中按预期工作;

Base ctor
Derived ctor2
Base ctor
Derived ctor2
Derived dtor2
Base dtor
Derived dtor2
Base dtor

到目前为止一切顺利。但是当我在发布模式下(使用所有本地编译器)构建它时,我得到了;

Base ctor
Derived ctor2
Base ctor
Derived ctor3
Derived dtor2
Base dtor

数组中的第二项在退出数组的生命周期时没有被破坏。 使其按我预期工作的唯一方法是使用 C++03 样式初始化或调试模式;

ItemList tmpList = {
    std::make_unique<Derived<int>>(2),
    std::make_unique<Derived<float>>(2.0f)
};

dummyFunc(tmpList);

这会导致预期的行为(调用所有析构函数)。

我还没有用任何其他编译器对此进行测试,但这是预期的行为吗?我做错了什么或者我错过了什么?

更新:

有趣的是,使用 Base 实例调用 dtors 是预期的;

dummyFunc(ItemList{
    std::make_unique<Base>(),
    std::make_unique<Base>()
});

输出;

Base ctor
Base ctor
Base dtor
Base dtor

并且只初始化数组(没有函数调用)的行为与函数调用相同。

您的代码实际上不是使用 g++ 6.2 构建的,错误非常明确,足以解释为什么您没有得到预期的行为:

foo.cc:44:15: error: taking address of temporary array
     dummyFunc(ItemList{
               ^~~~~~~~~
         std::make_unique<Derived<int>>(2),
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         std::make_unique<Derived<float>>(3.0f)
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     });

一个简单的解决方法是使用:

using ItemList = std::initializer_list<std::unique_ptr<Base>>;

而不是数组。

更新:VS 的行为可能是正确的:

An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

切换到 Visual Studio 2015。这可能是 VS2013 编译器中的一个实现错误。