【问题标题】:C++ perfect forward copy-only types to make_tupleC++ 将仅复制类型完美转发到 make_tuple
【发布时间】:2016-11-10 20:01:41
【问题描述】:

我在玩这个小sn-p:

#include <tuple>

struct copy_only
{
    copy_only() = default;
    copy_only(copy_only&&) = delete;
    copy_only(const copy_only&) = default;
};

template <typename ...Ts>
void foo(Ts&& ...xs)
{
    auto t = std::make_tuple(std::forward<Ts>(xs)...);
    (void) t;
}

int main()
{
   foo(copy_only{});
}

它可以与 gcc7 和 clang3.6、clang3.7、clang3.8 (Wandbox) 和 clang8.0 (macOS Sierra) 一起正常编译。它不能与 clang3.9、g++6.2 (macOS Sierra) 或 clang4.0 (Wandbox) 一起编译。他们都抱怨删除了移动构造函数。

它适用于仅移动类型。至少在 Wandbox 上可用的上述编译器上。

此代码是在 c++14 中将通用完美转发到元组的正确方法的示例吗?

【问题讨论】:

  • 如果去掉copy_only(copy_only&amp;&amp;) = delete;,就不会生成那个构造函数,可以选择复制重载。目前移动构造函数是一个更好的匹配。
  • @Jarod42 是正确的,但为什么它用 gcc 和旧的 clang 编译?
  • @kraz:在 c++1z 中,auto t = .. 会在不需要副本“存在”的情况下进行复制省略。
  • @Jarod42 感谢您的洞察力。我已经找到了解决方案(即仅在std::is_move_constructible&lt;Ts&gt;::value == false 时才呼叫前转)。但是我很好奇为什么上面的示例在可编译和不可编译之间切换,这是正确的 c++14:最新的 clang 或最新的 gcc。
  • @Jarod42 啊,现在说得通了

标签: c++ perfect-forwarding stdtuple


【解决方案1】:
    auto t = std::make_tuple(std::forward<Ts>(xs)...);

这确实是一种将参数转发到元组中的正确方法。

您得到的编译错误是由明确声明copy_only 的移动构造函数为已删除引起的。通常,如果您声明了一个复制构造函数,它将被省略,并且在移动上下文中将选择复制构造函数——就像它自 C++98 以来一样。但是因为你明确声明了它,它确实参与了重载决议,如果选择的话,会导致代码格式错误。

这是由 Howard Hinannt 提供的有用图表:

这可以通过从类定义中删除违规行来解决:

struct copy_only
{
    copy_only() = default;
    //copy_only(copy_only&&) = delete;
    copy_only(const copy_only&) = default;
};

现在,至于您的代码是否应该编译:据我所知,它应该编译。 tuple的移动构造函数定义为:

tuple(tuple&amp;&amp; u) = default;

要求is_move_constructible&lt;Ti&gt;::value 对所有 i 都是 true。

由于copy_only 不可移动构造,因此不应声明它也不应参与重载决议。

【讨论】:

  • 还有tuple实现也起作用。
  • @Jarod42 我刚看到你的评论。我不确定删除的移动构造函数是否意味着类型不可移动构造。
  • 这很乱。 is_move_constructible&lt;T&gt; 基本上是 is_constructible&lt;T, T&amp;&amp;&gt; 对于可引用类型,这对于 copy_only 是错误的,因为重载决议选择了一个已删除的 ctor。 OTOH,= default; 使事情变得复杂,因为如果移动子对象的重载解决方案选择了已删除的 ctor,则默认的移动 ctor 被定义为已删除,但这种定义为删除的默认移动 ctor 被忽略重载决议,如果发生这种情况,那么 is_move_constructible&lt;tuple&lt;copy_only&gt;&gt; 为真,因为它使用了复制 ctor。
  • ...并且可以说在这种情况下, move ctor 上的 requires 子句是无关紧要的,因为您从未实际使用过它。呃。
  • 这不是我要说的。如果移动子对象将使用已删除的构造函数,则默认的移动构造函数被隐式定义为已删除。而且这样的构造函数不参与重载决议。您的 test 没有子对象。
猜你喜欢
  • 1970-01-01
  • 2015-04-22
  • 2021-06-17
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
相关资源
最近更新 更多