‘explicit’关键字如何影响C++复制构造函数和函数参数?

huangapple go评论74阅读模式
英文:

How does the 'explicit' keyword affect C++ copy constructors and function parameters?

问题

The "explicit" 关键字用于修改复制构造函数可能会引发问题。作为函数参数传递的对象特别容易受到这些问题的影响。

这里是我的代码:

#include <iostream>
#include <string>

class Pig{
public:
    std::string _name;
public:
    Pig(std::string n) : _name(n) {}
    //~Pig() = default;
    explicit Pig(const Pig &other) {
        std::cout << "copy ctor!" << std::endl;
        this->_name = other._name;
    }
};

void show(Pig p) {
    std::cout << p._name << std::endl;
}

int main() {
    Pig pig{std::string("hello")};
    show(Pig{pig});     // 不通过
    // show(Pig(pig));  // 不通过
    return 0;
}

编译器版本:g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0。

上述提到的代码在c++14或更低版本下无法编译,但在c++17及更高版本下可以成功编译。

这里是编译器的错误:

test.cpp: In function int main():
test.cpp:22:7: error: cannot convert Pig to std::string {aka std::__cxx11::basic_string<char>}
   22 |  show(Pig{pig});     // 不通过
      |       ^~~~~~~~
      |       |
      |       Pig
  • 我知道如何使用 explicit,并希望显式调用复制构造函数以使其工作。

提前感谢您的帮助!

我尝试使用c++14和c++17编译。

英文:

The "explicit" keyword to modify the copy constructor can cause problems.
Objects passed as function parameters are particularly susceptible to these issues.

here are my codes:

#include &lt;iostream&gt;
#include &lt;string&gt;

class Pig{
public:
    std::string _name;
public:
    Pig(std::string n) : _name(n) {}
    //~Pig() = default;
    explicit Pig(const Pig &amp;other) {
        std::cout &lt;&lt; &quot;copy ctor!&quot; &lt;&lt; std::endl;
        this-&gt;_name = other._name;
    }
};

void show(Pig p) {
    std::cout &lt;&lt; p._name &lt;&lt; std::endl;
}

int main() {
    Pig pig{std::string(&quot;hello&quot;)};
    show(Pig{pig});     // no pass
    // show(Pig(pig));  // no pass
    return 0;
}

compiler version: g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0.

The code mentioned above does not compile with c++14 or lower versions,
but compiles successfully with c++17 and later versions.

here are the compiler's errors:

test.cpp: In function ‘int main()’:
test.cpp:22:7: error: cannot convert ‘Pig’ to ‘std::string’ {aka ‘std::__cxx11::basic_string&lt;char&gt;’}
   22 |  show(Pig{pig});     // 不通过
      |       ^~~~~~~~
      |       |
      |       Pig
  • I am aware of how to use explicit and I would like to call the copy constructor explicitly in order to make it work.

Thanks in advance!

I tried compiling with c++14 and c++17.

答案1

得分: 5

Prior C++17:

在C++17之前的问题是,当将参数Pig{pig}传递给show函数时,会出现参数的概念复制。也就是说,show函数的名为p的参数是从传递的参数Pig{pig}进行复制初始化,由于复制构造函数标记为explicit,因此会导致所述错误。

这可以从复制初始化中看出:

在以下情况下执行复制初始化

  • 将参数按值传递给函数

(emphasis mine)

C++17:

另一方面,从C++17开始,prvalue Pig{pig}会直接构造到变量p的存储中。也就是说,从C++17开始,不会有参数的概念复制,因此不需要复制构造函数是非显式的等等。

从C++17开始,我们有强制性复制省略

在以下情况下,即使复制/移动构造函数和析构函数具有可观察的副作用,编译器也必须省略类对象的复制和移动构造,对象将直接构造到否则将其复制/移动到的存储中。复制/移动构造函数不需要存在或可访问:

(emphasis mine)

请注意,Pig{pig}是一个prvalue,因此上述内容适用,并且复制构造函数不需要存在或可访问(可以是显式的)。

英文:

<h4> Prior C++17</h4>

The problem is that prior to C++17, there will be a conceptual copy of the argument Pig{pig} when passing it as an argument to the show function. That is, the parameter named p of the show function is copy initialized from the passed argument Pig{pig} and since the copy ctor is marked explicit this gives you the mentioned error.

This can be seen from copy initialization:

> Copy initialization is performed in the following situations:
> * When passing an argument to a function by value.

(emphasis mine)


<h4>C++17</h4>

OTOH starting with C++17 the prvalue Pig{pig} is constructed directly into the storage for p. That is, there is no conceptual copy of the argument from C++17 and therefore no need for the copy ctor to be non-explicit etc.

From C++17 we have mandatory copy elision:

> Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
>
> * In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:

(emphasis mine)

Note that Pig{pig} is a prvalue and so the above applies and the copy constructor doesn't need to be present or accessible(and it can be explicit).

huangapple
  • 本文由 发表于 2023年5月24日 22:09:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324481.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定