Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 复制初始化和直接初始化之间有区别吗?_C++_Initialization - Fatal编程技术网

C++ 复制初始化和直接初始化之间有区别吗?

C++ 复制初始化和直接初始化之间有区别吗?,c++,initialization,C++,Initialization,假设我有这个函数: void my_test() { A a1 = A_factory_func(); A a2(A_factory_func()); double b1 = 0.5; double b2(0.5); A c1; A c2 = A(); A c3(A()); } 在每个分组中,这些语句是否相同?或者在某些初始化中是否有额外的(可能是可优化的)副本 我见过有人说这两件事。请引用文本作为证据。也请添加其他案例。作业不同于初

假设我有这个函数:

void my_test()
{
    A a1 = A_factory_func();
    A a2(A_factory_func());

    double b1 = 0.5;
    double b2(0.5);

    A c1;
    A c2 = A();
    A c3(A());
}
在每个分组中,这些语句是否相同?或者在某些初始化中是否有额外的(可能是可优化的)副本


我见过有人说这两件事。请引用文本作为证据。也请添加其他案例。

作业不同于初始化

以下两行都进行初始化。完成单个构造函数调用:

A a1 = A_factory_func();  // calls copy constructor
A a1(A_factory_func());   // calls copy constructor
但这并不等于:

A a1;                     // calls default constructor
a1 = A_factory_func();    // (assignment) calls operator =
我目前没有文本来证明这一点,但很容易进行实验:

#include <iostream>
using namespace std;

class A {
public:
    A() { 
        cout << "default constructor" << endl;
    }

    A(const A& x) { 
        cout << "copy constructor" << endl;
    }

    const A& operator = (const A& x) {
        cout << "operator =" << endl;
        return *this;
    }
};

int main() {
    A a;       // default constructor
    A b(a);    // copy constructor
    A c = a;   // copy constructor
    c = b;     // operator =
    return 0;
}
#包括
使用名称空间std;
甲级{
公众:
A(){

cout这些情况中有很多都取决于对象的实现,因此很难给出具体的答案

考虑一下这个案例

A a = 5;
A a(5);
在这种情况下,假设一个合适的赋值运算符&初始化接受单个整数参数的构造函数,我如何实现所述方法会影响每一行的行为。然而,通常的做法是,其中一个在实现中调用另一个,以消除重复的代码(尽管在如此简单的情况下,没有真正的目的。)

<>编辑:如其他响应中提到的,第一行实际上将调用复制构造函数。将与赋值运算符相关的注释视为与单独赋值有关的行为。

也就是说,编译器如何优化代码将有它自己的影响。如果我让初始化构造函数调用“=”运算符,如果编译器没有进行优化,那么顶行将执行2次跳转,而不是底行中的一次跳转


现在,对于最常见的情况,您的编译器将在这些情况下进行优化,并消除此类低效。因此,您所描述的所有不同情况实际上都是相同的。如果您想确切了解正在执行的操作,可以查看编译器的目标代码或程序集输出。

doubleb1=0.5;
是构造函数的隐式调用

双b2(0.5);
是显式调用

查看以下代码以查看差异:

#include <iostream>
class sss { 
public: 
  explicit sss( int ) 
  { 
    std::cout << "int" << std::endl;
  };
  sss( double ) 
  {
    std::cout << "double" << std::endl;
  };
};

int main() 
{ 
  sss ddd( 7 ); // calls int constructor 
  sss xxx = 7;  // calls double constructor 
  return 0;
}
#包括
类sss{
公众:
显式sss(int)
{ 

std::cout第一个分组:它取决于
A_factory_func
返回的内容。第一行是复制初始化的示例,第二行是直接初始化。如果
A_factory_func
返回一个
A
对象,那么它们是等价的,它们都调用
A
的复制构造函数,否则第一个版本对于返回类型为
A\u factory\u func
或相应的
A
构造函数,sion从可用的转换运算符创建类型为
A
的右值,然后调用复制构造函数从该临时构造函数构造
a1
。第二个版本尝试找到一个合适的构造函数,该构造函数接受的任何内容e> _factory_func
返回,或获取返回值可以隐式转换为的内容

第二个分组:逻辑完全相同,只是内置类型没有任何外来构造函数,所以实际上它们是相同的

第三组:
c1
默认初始化,
c2
从临时初始化的值复制初始化。如果用户提供了默认构造函数(如果有的话),则具有pod类型的
c1
的任何成员(或成员的成员等)都可能不会初始化不要显式初始化它们。对于
c2
,这取决于是否有用户提供的复制构造函数,以及该构造函数是否适当地初始化了这些成员,但是临时的成员都将被初始化(如果没有显式初始化,则初始化为零).正如litb所指出的,
c3
是一个陷阱。它实际上是一个函数声明。

C++17更新 在C++17中,
A_factory_func()
的含义从创建临时对象(C++注意:

[12.2/1]
类类型的临时变量是在不同的上下文中创建的:……以及在某些初始化(8.5)中创建的。

即,用于复制初始化

[12.8/15]
当满足某些条件时,允许实现省略类对象的复制构造…

换句话说,一个好的编译器不会在可以避免的情况下为副本初始化创建副本;相反,它只会直接调用构造函数——也就是说,就像直接初始化一样

换句话说,在大多数情况下,复制初始化就像直接初始化一样,因为直接初始化可能会导致任意(因此可能未知)转换,所以我更喜欢在可能的情况下始终使用复制初始化。(还有一点是,它实际上看起来像是初始化。)

技术血腥: [12.2/1同上]
即使避免创建临时对象(12.8),也必须遵守所有语义限制,就像临时对象是创建的一样。


<>高兴的是我没有编写C++编译器。

关于这一部分的回答:

c2=A();c3(A())

由于大多数答案都是c++11之前的版本,我要补充c++11对此的看法:

简单类型说明符(7.1.6.2)或类型名说明符(14.6) 后跟括号中的表达式列表构造 给定表达式列表的指定类型。如果表达式列表是 单个表达式,类型转换表达式是等效的(在 定义,以及(如果定义有意义)对应的类型 表达式(5.4)。如果指定的类型是类类型,则类 类型应完整。如果表达式列表指定了多个 单一值,类型应为具有适当声明的 建造商(8.5、12.1)和t
A a1 = A_factory_func();
A a2(A_factory_func());
double b1 = 0.5;
double b2(0.5);
A c1;
A c2 = A();
A c3(A());
T t(x);
T t = x;
#include <iostream>
struct B;
struct A { 
  operator B();
};

struct B { 
  B() { }
  B(A const&) { std::cout << "<direct> "; }
};

A::operator B() { std::cout << "<copy> "; return B(); }

int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)
}
// output: <direct> <copy>
B(A const&)
B(A const&)
operator B(A&);
class A
{
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
};

class B
{
    explicit B(int) { }
    explicit B(int, int) { }
};
int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast

//  B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3 {4, 5};   // OK: direct-list-initialization selects B::B(int, int)
//  B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
}
A a1 = 1;        // this is copy initialization
A a2(2);         // this is direct initialization
B b2(2);        // this is direct initialization
B b5 = (B)1;    // not problem if you either use of assign to initialize and cast it as static_cast