什么时候允许编译器优化 auto+brace 样式初始化?
When is the compiler allowed to optimize auto+brace style initialization?
假设您有一个名为 Product 的 class,定义如下:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs);
Product(const Product &rhs);
~Product();
private:
const char *m_name;
int m_i;
};
然后你像这样初始化一个变量:
auto p = Product{"abc",123};
我认为标准规定编译器必须在逻辑上执行以下操作:
- 构造一个临时产品
- 移动构建 p(使用临时产品)
但是允许编译器对其进行优化,以便直接构造 p。
我验证了这一点(Visual Studio 2013),事实上,编译器优化了这一点,即使我们有自己的自定义(非默认)移动构造函数。这很好。
但是,如果我明确删除复制和移动构造函数,如下所示:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
~Product();
private:
const char *m_name;
int m_i;
};
auto+brace 初始化仍然可以编译。我虽然编译器必须阻止这种情况,因为不允许复制或移动。
奇怪的是,如果我将删除的复制和移动构造函数设为私有,如下所示:
class Product
{
public:
Product(const char *name, int i);
~Product();
private:
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
const char *m_name;
int m_i;
};
然后auto+brace初始化就不再编译了。
error C2248: 'Product::Product' : cannot access private member declared in class 'Product'
这是预期的行为吗?
这是 Visual Studio 2013(更新 3)中的错误吗?
注意:
我尝试在 ideone 上编译它,当复制和移动构造函数被删除(和 public)时,它确实拒绝编译初始化。所以我认为这是一个 Visual Studio 错误。
在你的行中
auto p = Product{"abc",123};
等号不是表示赋值运算符,而只是初始化程序的语法。因此,编译器并没有优化任何东西,只是在做初始化。
你之前说的标准很清楚,说明这是cl-compiler的bug。你永远无法确定,尽管如果一个编译器说了些什么而所有其他编译器都不同意,我希望这将是 MSVC 编译器的许多不符合标准的实现之一。
clang 3.7版本(svn-build)解读:
t.cpp:19:7:{19:11-19:30}: error: call to deleted constructor of 'Product'
[Semantic Issue]
auto p = Product{"abc", 123};
^ ~~~~~~~~~~~~~~~~~~~
t.cpp:8:2: note: 'Product' has been explicitly marked deleted here
[Semantic Issue]
Product(Product &&rhs) = delete;
^
1 error generated.
make: *** [t.o] Error 1
gcc 4.8解读:
t.cpp: In function ‘int main()’:
t.cpp:19:29: error: use of deleted function ‘Product::Product(Product&&)’
auto p = Product{"abc", 123};
^
t.cpp:8:2: error: declared here
Product(Product &&rhs) = delete;
^
make: *** [build/gcc/t.o] Error 1
另请记住,Explicitly Defaulted and Deleted Functions 是自 MSVC 2013 以来新增的,其实施尚未完成。就像它还不理解移动构造函数的=default。
我的猜测是 MSVC 2013 不检查移动构造函数或者只是回退到复制构造函数。
检查 MSVC 2015 可能会很有趣,因为它似乎有这些构造的(更)完整的实现。
JVApen
假设您有一个名为 Product 的 class,定义如下:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs);
Product(const Product &rhs);
~Product();
private:
const char *m_name;
int m_i;
};
然后你像这样初始化一个变量:
auto p = Product{"abc",123};
我认为标准规定编译器必须在逻辑上执行以下操作:
- 构造一个临时产品
- 移动构建 p(使用临时产品)
但是允许编译器对其进行优化,以便直接构造 p。
我验证了这一点(Visual Studio 2013),事实上,编译器优化了这一点,即使我们有自己的自定义(非默认)移动构造函数。这很好。
但是,如果我明确删除复制和移动构造函数,如下所示:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
~Product();
private:
const char *m_name;
int m_i;
};
auto+brace 初始化仍然可以编译。我虽然编译器必须阻止这种情况,因为不允许复制或移动。
奇怪的是,如果我将删除的复制和移动构造函数设为私有,如下所示:
class Product
{
public:
Product(const char *name, int i);
~Product();
private:
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
const char *m_name;
int m_i;
};
然后auto+brace初始化就不再编译了。
error C2248: 'Product::Product' : cannot access private member declared in class 'Product'
这是预期的行为吗? 这是 Visual Studio 2013(更新 3)中的错误吗?
注意: 我尝试在 ideone 上编译它,当复制和移动构造函数被删除(和 public)时,它确实拒绝编译初始化。所以我认为这是一个 Visual Studio 错误。
在你的行中
auto p = Product{"abc",123};
等号不是表示赋值运算符,而只是初始化程序的语法。因此,编译器并没有优化任何东西,只是在做初始化。
你之前说的标准很清楚,说明这是cl-compiler的bug。你永远无法确定,尽管如果一个编译器说了些什么而所有其他编译器都不同意,我希望这将是 MSVC 编译器的许多不符合标准的实现之一。
clang 3.7版本(svn-build)解读:
t.cpp:19:7:{19:11-19:30}: error: call to deleted constructor of 'Product'
[Semantic Issue]
auto p = Product{"abc", 123};
^ ~~~~~~~~~~~~~~~~~~~
t.cpp:8:2: note: 'Product' has been explicitly marked deleted here
[Semantic Issue]
Product(Product &&rhs) = delete;
^
1 error generated.
make: *** [t.o] Error 1
gcc 4.8解读:
t.cpp: In function ‘int main()’:
t.cpp:19:29: error: use of deleted function ‘Product::Product(Product&&)’
auto p = Product{"abc", 123};
^
t.cpp:8:2: error: declared here
Product(Product &&rhs) = delete;
^
make: *** [build/gcc/t.o] Error 1
另请记住,Explicitly Defaulted and Deleted Functions 是自 MSVC 2013 以来新增的,其实施尚未完成。就像它还不理解移动构造函数的=default。
我的猜测是 MSVC 2013 不检查移动构造函数或者只是回退到复制构造函数。
检查 MSVC 2015 可能会很有趣,因为它似乎有这些构造的(更)完整的实现。
JVApen