为什么我不能返回具有移动构造函数但其复制构造函数被显式删除的对象?

Why can't I return an object that has a move constructor but whose copy constructor is explicitly deleted?

提问人:依奈ちゃん 提问时间:1/3/2022 最后编辑:依奈ちゃん 更新时间:1/3/2022 访问量:173

问:

我有一个函子,其声明如下所示:

class logger {
public:
    log_t operator() (unsigned _LogLevel) {
        return log_t{_LogLevel};
    }
};

就像在代码中一样,我希望这个函子构造并返回一个log_t对象。但是编译器抱怨 log_t 对象的复制构造函数已被删除。

我承认,类类型 log_t 没有复制构造函数,只有一个移动构造函数和一个带有一个参数的显式普通构造函数。由于log_t是从标准库模板类basic_ostream派生而来的,并且该类的复制构造函数已被显式删除,因此我只能提供移动构造函数来log_t对象。

此外,由于其生命周期要求,此对象不能使用 static 返回引用。

我添加了编译选项。在我看来,无论是否打开 RVO 选项,根据 C++11 标准,函数返回值都应该优先调用移动构造函数而不是复制构造函数。在这个模仿函数中,应该不需要复制构造函数,但它实际上会报告一个错误。-std=c++11

#include <iosfwd>
#include <ostream>    // for template class 'basic_ostream'

class mlog_t 
  : public std::basic_ostream
   <char, std::char_traits<char> > {
         
public:
    explicit   mlog_t (unsigned) noexcept{}
               mlog_t (mlog_t&&) noexcept{}
               mlog_t (mlog_t const&) = delete;
              ~mlog_t () noexcept{}
    mlog_t& operator= (mlog_t const&) = delete;

private:
   /**
    *  This is the handle to the buffer.
    */
    mutable void* instance_ptr;
};
    
class __mcl_mlog_t_ {
public:
    mlog_t operator() (unsigned _LogLevel) noexcept{
        return mlog_t{_LogLevel};
    }
};
    
__mcl_mlog_t_ mlog;

int main (){
    return 0;
}

我收到编译错误:

25  32  C:\Users\test.cpp   [Error] use of deleted function 'mlog_t::mlog_t(const mlog_t&)'

我尝试了以下答案,但我失败了: “为了利用C++11的这个特性,构造函数(在这种情况下采用int)必须是非显式的。 (我们可以按函数的值返回具有已删除/私有复制/移动构造函数的对象吗? )

此错误的原因是什么?我该如何解决这个问题?

C++ C++11 复制构造函数 return-value-optimization

评论

5赞 HolyBlackCat 1/3/2022
不要描述你的代码,展示它!我们需要一些东西,我们可以插入编译器并看到与您相同的错误。
1赞 Louis Go 1/3/2022
在这种情况下,需要一个最小的可重现示例。我建议使用 godbolt.org
2赞 StoryTeller - Unslander Monica 1/3/2022
也许你假设有一个移动构造函数是完全错误的。log_t
0赞 依奈ちゃん 1/3/2022
谢谢。我已将原始代码的简化版本添加到该问题中。
2赞 Daniel Langr 1/3/2022
无法重现:godbolt.org/z/eWEoPrqrK。顺便说一句,保留包含双下划线的标识符:eel.is/c++draft/lex#name-3.1。另请注意,它不提供默认构造函数。std::basic_ostream

答:

0赞 Paul 1/3/2022 #1

在 C++17 之前不能保证复制省略。 在此之前,大多数编译器都会省略副本,但直到 C++17 才成为标准的一部分。

尝试将您的标志更改为 .-std=c++17

参考

请特别注意有关可能重叠的子对象的段落,因为您写的是从 .basic_ostream

评论

2赞 StoryTeller - Unslander Monica 1/3/2022
即使没有复制省略,仍然可以返回可移动的字体。OP 的片段和描述表明应该没有问题。这个答案跳了枪。
0赞 依奈ちゃん 1/3/2022
谢谢。我检查了 ostream 库的定义,并注意到我的编译器中的标准库在类 basic_ostream 中包含一个已删除的移动构造函数......它只是不能很好地支持 C++11 标准。所以我尝试使用一个很好地支持C++ 17的编译器,然后它起作用了。
0赞 Paul 1/4/2022
StoryTeller-UnslanderMonica,你是对的,在找到参考资料并重新阅读问题后,我意识到了这一点。然而,参考显然导致了实际问题的发现,所以我暂时不按原样回答。桃原ゆきな你能检查一下它是否也可以使用较新的编译器但在C++11模式下工作吗?正如 StoryTeller-UnslanderMonica 所写的那样,它应该可以工作,因为应该移动可移动的活字。