c++复制初始化和直接初始化,奇怪的情况

标签 c++ constructor copy-constructor explicit-constructor

在继续阅读本文之前,请阅读 Is there a difference in C++ between copy initialization and direct initialization?首先,确保您了解它在说什么。

这里我先总结一下规则(阅读标准n3225 8.5/16、13.3.1.3、13.3.1.4和13.3.1.5),

1) 对于直接初始化,所有的构造函数都会被认为是重载集,重载决议会根据重载决议规则选择最好的。

2) 对于复制初始化且源类型与目标类型相同或派生自目标类型,规则与上述相同,只是只有转换构造函数(没有显式的构造函数)将被视为重载集。这实际上意味着显式复制/移动构造函数不会被考虑到重载集中。

3)对于上面(2)中未包含的复制初始化情况(源类型与目标类型不同,并且不是从目标类型派生的),我们首先考虑可以从源类型转换为目标的用户定义转换序列类型或(当使用转换函数时)到其派生类。如果转换成功,则结果用于直接初始化目标对象。

3.1) 在此用户定义的转换序列中,根据 8.5/16 和 13.3.1.4 中的规则,将同时考虑转换 ctors(非显式 ctors)和非显式转换函数。

3.2)结果纯右值将直接初始化目标对象,如(1)中所列规则,见8.5/16。

好吧,规则说完了,让我们看一些奇怪的代码,我真的不知道我的推理哪里错了,或者只是所有的编译器都错了。请帮助我,谢谢。

struct A
{
    A (int) { }
    A() { }
    explicit A(const A&) { }
};
struct B
{
    operator A() { return 2; }
    //1) visual c++ and clang passes this
    //gcc 4.4.3 denies this, says no viable constructor available
};
int main()
{
    B b;
    A a = b;
    //2) oops, all compilers deny this
}

据我了解,对于(1),

operator A() { return 2; }

因为C++有一个函数return作为copy-initialization的规则,根据上面的规则,2会先隐式转换为A,这样应该可以,因为A有一个构造函数A(int)。然后转换后的临时纯右值将用于直接初始化返回的对象,这也应该没问题,因为直接初始化可以使用显式复制构造函数。所以 GCC 是错误的。

对于(2),

A a = b;

按我的理解,首先b通过operator A()隐式转换为A,然后转换后的值直接初始化a,当然可以调用显式拷贝构造函数?因此这应该通过编译并且所有编译器都是错误的?

请注意,对于 (2),visual c++ 和 clang 都有类似的错误, “错误,无法从 B 转换为 A”,但是如果我删除 A 的复制构造函数中的显式关键字,错误就消失了。

感谢阅读。


编辑 1

因为还是有人没看懂我的意思,我引用8.5/16的以下标准,

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

请注意,它确实提到了在用户定义的转换之后直接初始化。这意味着,在我的理解中,以下代码应遵守我评论的规则,clang、coomeau online、visual c++ 都证实了这一点,但 GCC 4.4.3 不符合 (1) 和 (2)。虽然这是一个奇怪的规则,但它遵循了标准的推理。

struct A
{
    A (int) { }
    A() { }
    explicit A(const A&) { }
};

int main()
{
    A a = 2;    //1)OK, first convert, then direct-initialize
    A a = (A)2; //2)oops, constructor explicit, not viable here!
}

最佳答案

您声明了您的复制构造函数显式(顺便说一句,为什么?),这意味着它不能再用于类对象的隐式复制。为了使用此构造函数进行复制,您现在必须使用直接初始化语法。见 12.3.1/2

2 An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used.

这个问题可以用下面更短的例子来说明

struct A {
  A() {}
  explicit A(const A&) {}
};

int main() {
  A a;
  A b = a; // ERROR: copy-initialization
  A c(a); // OK: direct-initialization
}

这就是阻止所有转换工作的原因,因为它们都依赖于复制初始化,而复制初始化又依赖于隐式复制。并且您禁用了隐式复制。

此外,请参阅 Defect Report #152其中涵盖了这个特定问题。虽然我不确定“提议的决议”的后果应该是什么......

关于c++复制初始化和直接初始化,奇怪的情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4801136/

相关文章:

c++:如何编写一个类似 std::bind 的对象来检查多余的参数?

java - Java 中比较器接口(interface)的构造函数

c++ - 检查是否调用了复制构造函数

c++ - 生成有关使用复制构造函数和赋值的警告

c++ - Hadoop管道(wordcount)示例失败(hadoop 0.21.0)

c++ - DirectX 11 E_INVALIDARG 在设备创建期间

c++ - 编写构造函数/析构函数是好的做法吗?

c++ - Cpp 在不需要时询问默认构造函数

c++ - 当类包含指针时如何创建复制构造函数

c++ - 将 float 作为指向矩阵的指针传递