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 编译器中的一个实现错误。
我正在尝试将一个数组传递给一个函数并防止每次都写入 "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 编译器中的一个实现错误。