默认构造函数阻止调用 emplace_back

Posted

技术标签:

【中文标题】默认构造函数阻止调用 emplace_back【英文标题】:Default constructor prevents from calling emplace_back 【发布时间】:2016-01-25 21:08:28 【问题描述】:

似乎添加默认构造函数会阻止调用emplace_back 并产生错误消息:“静态断言失败:类型不可分配”(gcc 5.3 with -std=c++14)。这是一个说明问题的简单代码:

class A 
public:
    int a;
    A() = default;
    A(int a) 
        this->a = a;
    
    A(A const & a) = delete;
    A& operator =(A const & a) = delete;
    A(A && a) = default;
    A& operator =(A && a) = default;
;

int main() 

    A a(4);
    std::vector<A> vec;
    vec.emplace_back(std::move(a)); // Error: type is not assignable
    return 0;

删除默认构造函数时,错误消失了!此外,如果定义了默认构造函数(即使它什么都不做),错误也会消失:

class A 
public:
    int a;
    A() 
    
    A(int a) 
        this->a = a;
    
    A(A const & a) = delete;
    A& operator =(A const & a) = delete;
    A(A && a) = default;
    A& operator =(A && a) = default;
;

int main() 

    A b;
    A a(4);
    std::vector<A> vec;
    vec.emplace_back(std::move(a)); // Error gone
    return 0;

似乎 "A() = default;"是导致问题的原因。 这是编译器部分的正常行为还是错误?

【问题讨论】:

clang 显示与 gcc 相同的行为 代码在c++11下使用gcc 4.7.2编译良好​​span> 影响课堂的琐碎性。 如果您使用 libc++,@jaggedSpire clang 会编译代码。这看起来像是 libstdc++ 的一个错误,它可能将针对琐碎类型的 emplace_back 优化为 memcpy 调用,并且该调用链涉及调用一个断言该类型是可复制分配的函数。 @rustyx, A() = default; 可能是微不足道的,但A() 绝对不是。这可能会影响优化(在这种情况下,尽管 std::lib 的优化尝试存在错误)。 【参考方案1】:

这是一个 libstdc++ 错误(编辑:报告为 bug 69478)。

简而言之,libstdc++ 的std::vector,与此处相关,使用std::uninitialized_copy(与移动迭代器配对)在重新分配时移动元素,如果类型是微不足道的并且迭代器的引用类型是可分配的,则它减少为std::copy (即,概念上使用的赋值运算符是可用的)。

然后,指向普通类型的指针的std::copy(或者在我们的例子中,一个包装指针的move_iterator)反过来被优化为对memmove的调用以及对is_copy_assignable的检查。当然,在这种情况下检查是错误的,因为 uninitialized_copy 与移动迭代器配对,只要求事物是可移动构造的。

如果您没有默认构造函数或者默认构造函数是用户定义的,那么该类就不是微不足道的,因此您不会点击触发此错误的代码路径。

【讨论】:

感谢您的回答。

以上是关于默认构造函数阻止调用 emplace_back的主要内容,如果未能解决你的问题,请参考以下文章

学习 emplace_back() 和 push_back 的区别 emplace_back效率高

emplace_back() 和 push_back 的区别

[CPP]push_back和emplace_back的区别

STL:emplace_back() 和 push_back 的区别

C++ emplace_back()是什么

emplace_back优势在哪里?