如果结构体包含互斥锁,则 C++ 将初始值设定项列表推送到标准 vector 时出现问题

标签 c++ mutex clang++ initializer-list deleted-functions

我目前正在开发一个 C++ 项目,其中我有一个存储在 vector 中的结构列表,这些结构有很多与之相关的处理。为了加快速度,我选择将程序拆分为多个线程,而我选择的懒惰方法是向标准库 vector 中的每个结构添加互斥体。这样我就可以让多个线程迭代数组,并通过调用 mutex.try_lock() 基本上获得各个元素的所有权,并完成与该元素的关联处理,或者移至下一个打开的元素。

在我们开始之前,请注意以下内容实际上有效。

#include <mutex>
#include <vector>

struct foo {
    int a;
    std::mutex b;
};

void working_demo () {
    // assign by initializer list
    foo f = {.a = 1};
}

int main () {
    working_demo();
}

因此,我打算以与上面非常相似的方式填充我的标准 vector ,但它不起作用。

#include <mutex>
#include <vector>

struct foo {
    int a;
    std::mutex b;
};

void broken_demo () {
    std::vector<foo> bar;

    // assign by initializer list
    bar.push_back({.a = 1});
}

int main () {
    broken_demo();
}

编译器错误:

clang++ -std=c++11 -Wall -Wextra -Wfatal-errors -pedantic -I./  -c -o demo.o demo.cpp
demo.cpp:13:20: warning: designated initializers are a C99 feature [-Wc99-extensions]
    bar.push_back({.a = 1});
                   ^~~~~~
In file included from demo.cpp:1:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/mutex:38:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/tuple:39:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/array:39:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/stdexcept:39:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/string:41:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/allocator.h:46:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/x86_64-pc-linux-gnu/bits/c++allocator.h:33:
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/ext/new_allocator.h:146:8: fatal error: call to implicitly-deleted copy constructor of 'foo'
                            _Up(std::forward<_Args>(__args)...)))
                            ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/alloc_traits.h:483:24: note: in instantiation of exception specification for
      'construct<foo, foo>' requested here
        noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...)))
                              ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/vector.tcc:115:21: note: in instantiation of exception specification for 'construct<foo, foo>'
      requested here
            _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                           ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_vector.h:1201:9: note: in instantiation of function template specialization
      'std::vector<foo, std::allocator<foo> >::emplace_back<foo>' requested here
      { emplace_back(std::move(__x)); }
        ^
demo.cpp:13:9: note: in instantiation of member function 'std::vector<foo, std::allocator<foo> >::push_back' requested here
    bar.push_back({.a = 1});
        ^
demo.cpp:6:16: note: copy constructor of 'foo' is implicitly deleted because field 'b' has a deleted copy constructor
    std::mutex b;
               ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/std_mutex.h:94:5: note: 'mutex' has been explicitly marked deleted here
    mutex(const mutex&) = delete;
    ^

我相当肯定这说明这不起作用的原因是该结构正在尝试调用互斥体的复制构造函数,而实际上互斥体没有复制构造函数。我明确不想这样做。

我最初的想法是,为了确保它甚至不会尝试调用互斥体的复制构造函数,我可以为我的类创建自己的构造函数,并且基本上显式地省略互斥体的复制。此方法看起来像这样 - 但它也不起作用。

#include <mutex>
#include <vector>

struct foo {
    foo (int A): a(A) {;}

    int a;
    std::mutex b;
};

void broken_demo () {
    std::vector<foo> bar;

    // assign by initializer list
    bar.emplace_back(1);
}

int main () {
    broken_demo();
}

编译器错误:

clang++ -std=c++11 -Wall -Wextra -Wfatal-errors -pedantic -I./  -c -o demo.o demo.cpp
In file included from demo.cpp:2:
In file included from /usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/vector:65:
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_construct.h:75:38: fatal error: call to implicitly-deleted copy constructor of 'foo'
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
                                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:83:8: note: in instantiation of function template specialization
      'std::_Construct<foo, foo>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:134:2: note: in instantiation of function template specialization
      'std::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<foo *>, foo *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:289:19: note: in instantiation of function template specialization
      'std::uninitialized_copy<std::move_iterator<foo *>, foo *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/stl_uninitialized.h:310:19: note: in instantiation of function template specialization
      'std::__uninitialized_copy_a<std::move_iterator<foo *>, foo *, foo>' requested here
      return std::__uninitialized_copy_a
                  ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/vector.tcc:473:10: note: in instantiation of function template specialization
      'std::__uninitialized_move_if_noexcept_a<foo *, foo *, std::allocator<foo> >' requested here
                = std::__uninitialized_move_if_noexcept_a
                       ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/vector.tcc:121:4: note: in instantiation of function template specialization 'std::vector<foo,
      std::allocator<foo> >::_M_realloc_insert<int>' requested here
          _M_realloc_insert(end(), std::forward<_Args>(__args)...);
          ^
demo.cpp:15:9: note: in instantiation of function template specialization 'std::vector<foo, std::allocator<foo> >::emplace_back<int>' requested here
    bar.emplace_back(1);
        ^
demo.cpp:8:16: note: copy constructor of 'foo' is implicitly deleted because field 'b' has a deleted copy constructor
    std::mutex b;
               ^
/usr/sup/bin/../lib/gcc/x86_64-pc-linux-gnu/9.1.0/../../../../include/c++/9.1.0/bits/std_mutex.h:94:5: note: 'mutex' has been explicitly marked deleted here
    mutex(const mutex&) = delete;
    ^
1 error generated.

对于解决这个问题有什么想法吗?我希望保持代码相对简单,但我不确定如何使标准 vector 表现良好,或者至少正确使用它。

最佳答案

问题是在您的代码中 foo 实例是按值传递的。因此,当您将某些内容放入 vector 中时,需要创建一个拷贝。避免这种情况的一个简单方法是将指向 foo 的指针放入 vector 中。您可以将指针包装到某种引用计数机制中,这样您就不必跟踪释放对象。

这是一个使用 std::unique_ptr 的简短示例,当 vector 超出范围时,它将自动删除 vector 中的所有 foo 实例:

#include <mutex>
#include <vector>
#include <memory>
#include <iostream>
#include <functional>

struct foo {
   foo(int A) : a(A) {}
   int a;
   std::mutex b;
};

void fixed_demo () {
   std::vector<std::unique_ptr<foo>> bar;

   std::unique_ptr<foo> p(new foo(1));
   bar.push_back(std::move(p));
   bar.emplace_back(new foo(2));
   for (auto it = bar.begin(); it != bar.end(); ++it) {
      (*it)->b.lock();
      std::cout << (*it)->a << std::endl;
      (*it)->b.unlock();
   }
}

int main () {
   fixed_demo();
   return 0;
}

关于如果结构体包含互斥锁,则 C++ 将初始值设定项列表推送到标准 vector 时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58908524/

相关文章:

c++ - 是什么原因导致在使用[&] capture子句调用lambda时导致奇怪的行为,在该子句中C++中使用了当前作用域之外的变量?

c++ - openmp是否有可能在同一个cpu(核心)上运行不同的线程

c++ - 函数需要抽象类型 A 的 C++ 对象。如何将 A 子类的 Lua 对象传递给它?

c - 如何在 FreeBSD/Mac OS X 中从 Mutex 对象获取拥有的线程 ID

c++ - 使用 c++1z 在 <functional> 上出现 clang 4 构建错误

c++ - 高斯模糊图像处理c++

c++ - 如果在获得互斥锁后发生中断会怎样

c++ - 如果另一个线程将元素推送到 vector 的末尾, vector 上的迭代器会失效吗?

clang++ - 用于检查 'linker'(通过 clang++)在 Mac OS 上创建可执行文件的命令

c++ - Clang Diagnostics,如何忽略编译器特定的扩展