【问题标题】:C++11 array initialization with a non-copyable type with explicit constructor具有显式构造函数的不可复制类型的 C++11 数组初始化
【发布时间】:2014-05-20 13:19:00
【问题描述】:

我有一个不可复制的(第三方)类。我想初始化它们的数组。这是我最好的尝试:

#include <array>

class Thing
{
public:
  explicit Thing(int) {}
  Thing(const Thing&) = delete;
};

int main()
{
  std::array<Thing, 1> things{{{100}}}; // error here
};

GCC 4.7.2 说:

错误:转换为'std::array::value_type 初始化列表中的 {aka Thing}' 将使用显式构造函数 ‘事物::事物(int)’

好的,但这正是我想要的——使用显式构造函数。我该如何表达?如果我真的自己调用构造函数,那么我会收到一个关于复制构造函数被删除的错误。而且我不能使用std::move(),因为 Thing 是不可移动的(我不能修改它)。

到目前为止,我发现的唯一替代方法是 https://stackoverflow.com/a/15962814/4323,但这是不可取的,因为它是一堆额外的代码,而且我需要在使用它的任何地方强制转换“存储”(或保留一个单独的指向它的指针,这添加了我不想要的间接)。

我想要一个解决方案,在实际使用 Things 时提供最佳性能,而不需要很多丑陋的样板。

【问题讨论】:

  • 你可以添加一个显式的构造函数来接受initializer_list&lt;int&gt; ?
  • 标准规定对于std::array&lt;T, N&gt;T 必须是 MoveConstructible 和 MoveAssignable
  • 链接线程中的解决方案看起来并不太痛苦。无论如何,您必须具有一定程度的间接性(我认为访问 things[n] 访问 std::array 与访问 things[n] 没有任何不同,其中 Things *things; 指向您放置 new 的存储)
  • @MattMcNabb:我希望它是不同的,因为数组本身的地址在编译时是已知的,但是存储在指针中的地址可能不会被编译器以同样的方式理解。我可能最终只是在调用站点将对齐的存储转换为数组类型(当然是在一个函数中)。我没有听说 std::array 的 value_type 必须是可移动的......它似乎工作得很好,只要 value_type 的构造函数不明确。

标签: c++ arrays c++11 initialization noncopyable


【解决方案1】:

C++17 的 guaranteed copy elision 再次发挥作用:像 Thing{100} 这样的表达式不再创建一个对象,而只是指定如何其他对象(您的数组元素)将被创建。

【讨论】:

  • 你知道现在有什么编译器可以编译我的原始代码吗?我在godbolt.org(GCC 主干,Clang 5.0)中尝试了一些东西,但它们都不起作用,即使使用-std=c++17
  • 你必须添加Thing:然后你会得到wandbox.org/permlink/a6QUxzY3O0xN1GJ6
【解决方案2】:

我尝试添加默认的移动 ctor 和移动赋值运算符,稍微更改了初始化并编译:

#include <array>

class Thing
{
public:
  explicit Thing(int) {}
  Thing(const Thing&) = delete;
  Thing(Thing&&) = default;
  Thing& operator=(Thing&&) = default;
};

int main()
{
    std::array<Thing, 1> things {{ Thing(100) }}; // error gone
}

编辑:我错过了“第三方”部分。对不起,如果这没有帮助:)

【讨论】:

  • 是的,使类可移动修复它,就像使构造函数不显式一样。但我也做不到,因为这不是我的课。我想我可以用一个具有隐式构造函数的类来包装这个类......
  • 添加移动构造函数并不能解决使用 new[] 运算符的情况。例如:auto things = new Thing[1] { 100 }; 不起作用,auto things = new Thing[1] { Thing { 100 } }; 也不起作用。
【解决方案3】:

您可以使用std::vector

std::vector<Thing> things;
things.reserve(1);
things.emplace_back(100);

或者只针对一个元素boost::optional:

boost::optional<Thing> thing;
thing.emplace(100);

【讨论】:

    猜你喜欢
    • 2015-06-09
    • 1970-01-01
    • 2020-12-27
    • 2014-12-28
    • 2016-10-02
    • 2012-11-15
    • 2012-08-16
    • 1970-01-01
    相关资源
    最近更新 更多