【问题标题】:Why can't std::tuple be element-wise constructed with a std::tuple of compatible types?为什么 std::tuple 不能用兼容类型的 std::tuple 逐元素构造?
【发布时间】:2016-11-11 21:53:20
【问题描述】:

我无法从兼容类型的std::tuple 逐元素初始化std::tuple 元素。为什么它不能像 boost::tuple 那样工作?

#include <tuple>
#include <boost/tuple/tuple.hpp>

template <typename T>
struct Foo
{
    // error: cannot convert 'std::tuple<int>' to 'int' in initialization
    template <typename U>
    Foo(U &&u) : val(std::forward<U>(u)) {}

    T val;
};

int main()
{
    boost::tuple<Foo<int>>{boost::tuple<int>{}};    // ok

    auto a = boost::tuple<int>{};
    boost::tuple<Foo<int>>{a};                      // ok

    std::tuple<Foo<int>>{std::tuple<int>{}};        // fails with rvalue

    auto b = std::tuple<int>{};
    std::tuple<Foo<int>>{b};                        // fails with lvalue
}

Live on Coliru(GCC 或 Clang 和 libstdc++ 无法编译,但是 Clang 和 libc++ 编译没有错误


std::tuple 没有进行元素构造,它实例化了Foo&lt;int&gt;::Foo&lt;std::tuple&lt;int&gt;&gt; 而不是Foo&lt;int&gt;::Foo&lt;int&gt;。我认为std::tuple::tuple overloads no. 4 and 5 正是为此目的:

template <class... UTypes>
tuple(const tuple<UTypes...>& other);

template <class... UTypes>
tuple(tuple<UTypes...>&& other);

注意:

不参与重载决议,除非
std::is_constructible&lt;Ti, const Ui&amp;&gt;::value 对于所有i 都是true

std::is_constructible&lt;Foo&lt;int&gt;, int&gt;::valuetrue。从 GCC 模板错误中,我可以看到重载号。 3:

template <class... UTypes>
explicit tuple(UTypes&&... args);
而是选择了

。为什么?

【问题讨论】:

  • 好的,它不适用于 -std=libstdc++,但适用于 Clang 上的 -std=libc++。必须是实施问题。
  • 请在 gcc 的 bugzilla 中提交一个错误。
  • 这个问题仍然会为遇到同样问题的人提供有价值的信息。它会告诉他们问题出在标准库实现上,而不是在他们的代码中。有许多常见的欺骗目标是对特定实现错误的解释(例如 MinGW 和 stoi

标签: c++ templates c++11 stdtuple boost-tuples


【解决方案1】:

当通过 tuple&amp; 时,重载 (4) 和 (5) 的匹配比 (3) 差:它们是 const&amp;&amp;&amp; 重载,而 (3) 通过完美转发的魔力完全匹配。

(3) 是有效的,因为您的 Foo(U&amp;&amp;) 构造函数过于贪婪。

将 SFINAE 检查添加到 Foo(U&amp;&amp;),以便在构建失败时无法匹配:

template <class U,
  std::enable_if_t<std::is_convertible<U,int>{},int>* =nullptr
>
Foo(U &&u) : val(std::forward<U>(u)) {}

然而,右值的情况应该是有效的或者是模棱两可的。查看您的实时示例的错误日志,我看到的唯一错误是左值错误。

【讨论】:

  • 哇,好用! (我改成typename = std::enable_if_t&lt;std::is_convertible&lt;U, T&gt;::value&gt;) 为什么libstdc++和libc++有区别?这是标准未指定的实现细节吗?我们应该向谁报告?
  • 对于匹配问题:const auto b 仍然重现。您需要将此报告给 libstdc++(即 gcc 的 bugzilla)。此外,您可能想在 isocpp.org 论坛上询问有关添加采用非常量元组左值的构造函数的问题,因此它是一个更好的匹配。
  • @logic 感谢您修复错别字,但class= 解决方案比int*=nullptr 更糟糕。在多次重载的情况下,class= 会失败,而int*= 不会。
  • @Yakk 主要是我想参与T 而不是int。在这种情况下它是否必须产生int*?不能是void*吗?
  • @LogicStuff void* 不是标准下的合法模板非类型参数类型。 int* 是。我们总是将其设置为nullptr,它仅出于 SFINAE 的原因而存在,它是比class= 模式更好的 SFINAE 系统。 int 可以是任何非 void 类型,恰好在这种特殊情况下 C++ 禁止 void*。一些编译器未能强制执行该禁令,但我相信当成本是 4 个额外字符时编写符合标准的代码。同样,int 类型从未使用过
猜你喜欢
  • 2019-06-15
  • 2014-08-31
  • 1970-01-01
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
相关资源
最近更新 更多