“explicit”关键字如何影响C++的复制构造函数和函数参数?

4
"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});     // no pass
    // show(Pig(pig));  // no pass
    return 0;
}

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

上述代码无法使用c++14或更低版本编译,但可以成功使用c++17及更高版本编译。

以下是编译器的错误:

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

提前致谢!

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


1
这里有一个复现链接:https://godbolt.org/z/7Gvr49GPn - Marek R
1
@Dharmesh946嗯,我可以重现这个问题:https://godbolt.org/z/vnTjaz7bE; 这与复制省略有关。在我看来,你应该始终使用void show(Pig const& p)作为函数的签名... - fabian
@fabian 确实,这段代码在 C++14 中会失败。可以通过将 Pig 作为右值引用传递到 show 中来修复它。我不知道通过值传递 rvalue 的规则有什么区别... - Dharmesh946
2
@Dharmesh946 区别在于,在一个情况下,您将传递对由表达式Pig{pig}创建的临时对象的引用,在另一种情况下,您将需要从该临时对象中复制构造函数参数值; 对于 >= C++17,此复制已被删除,因为进行了复制省略。 - fabian
1
“explicit”关键字如何影响C++的复制构造函数和函数参数?编译器不能再将“explicit”复制构造函数用于“implicit”复制,这将导致大量问题而没有任何好处。 - Eljay
显示剩余3条评论
1个回答

5

C++17 之前

问题在于,在 C++17 之前,将参数 Pig{pig} 作为参数传递给 show 函数时,会有一个概念上的拷贝。也就是说,show 函数的名为 p 的参数是从传递的参数 Pig{pig} 进行拷贝初始化,由于拷贝构造函数被标记为 explicit,因此会出现上述错误。

这可以从 拷贝初始化 中看到:

在以下情况下执行拷贝初始化:

  • 通过值传递参数给函数时

(强调是我的)


C++17

从C++17开始,prvalue Pig{pig} 直接构造到p的存储中。也就是说,从C++17开始,没有概念上的参数副本,因此不需要复制构造函数为非显式等。

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

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

  • 在对象初始化时,当初始化表达式是与变量类型相同的prvalue(忽略cv限定符)时:

(我强调)

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接