复制参数在不应调用该构造函数时调用已删除的构造函数

时间:2017-09-14 19:02:48

标签: c++ c++14 move c++17 deleted-functions

#include <memory>

template <typename T>
class Wrapper {
public:
    Wrapper() = delete;
    Wrapper(const Wrapper&) = delete;
    Wrapper(Wrapper&&) = delete;

    ~Wrapper() = default;

    Wrapper(const T&) = delete;
    Wrapper(T&& in) : instance{std::move(in)} {}

    T instance;
};

void foo(Wrapper<std::shared_ptr<int>>) {}

int main() {
    auto ptr = std::make_shared<int>(1);
    foo(std::move(ptr));
}

这一直在C ++ 17中工作,所以我从来没有想过,但为什么这段代码尝试在C ++ 14中调用移动构造函数?不应该在函数参数中构造它吗?这似乎不是c ++ 17的问题,但不是用c ++ 14编译。

我看到的唯一解决方法是将foo参数设为右值,但是如果不使foo中的参数成为C ++ 14中的右值,我可以做些什么来使其工作?

我的第一个想法是,临时必须是构造函数才能传递给函数,但更令人惊讶的是,即使使用-fno-elide-constructors并取消删除移动构造函数和复制构造函数,这些也不会似乎被称为!这是gcc和clang中的一个错误吗?

有关错误,请参阅https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 并查看奇怪的行为https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz

1 个答案:

答案 0 :(得分:4)

当您致电foo(std::move(ptr));时,您没有给它Wrapper<std::shared_ptr<int>>。因此,编译器生成一个临时的,并使用它来构造foo的参数。现在,这可以省略,我们可以直接构造Wrapper<std::shared_ptr<int>>但移动/复制构造函数仍然需要可访问,即使它从未被调用过。

使用C ++ 17,这种情况不再发生。我们有guaranteed copy elision这意味着没有临时实现,而是直接构造参数。