当前位置: 首页 > 知识库问答 >
问题:

从std::tuple派生的类的直接初始化失败,而它对std::pair有效

颜欣怡
2023-03-14

有时,我想通过一个真正的类来更改我定义的类型。

例如,我在这里举了一个例子,如何以与类型相同的方式使用结构:

using t_int_pair = std::pair< int, int >;
struct s_int_pair : public std::pair< int, int >
{
    using std::pair< int, int >::pair; // inherit ctor
};

void foo()
{
    auto [a1, a2] = t_int_pair{ 0, 0 };
    auto [b1, b2] = s_int_pair{ 0, 0 };
    // ...
}

这工作正常。

但是:由于任何原因,相同的代码不适用于std::元组,即这会产生编译错误:

using t_int_triple = std::tuple< int, int, int >;
struct s_int_triple : public std::tuple< int, int, int >
{
    using std::tuple< int, int, int >::tuple; // inherit ctor
};

void bar()
{
    auto [a1, a2, a3] = t_int_triple{ 0, 0, 0 };
    auto [b1, b2, b3] = s_int_triple{ 0, 0, 0 }; // ERROR: cannot decompose class type 'std::_Tuple_impl<0, int, int>'
    // ...
}

有人知道为什么会这样吗?

有办法解决这个问题吗?

我用clang、gcc和msvc在编译器资源管理器上测试了这个。

感谢您的帮助,

问候,

佐波

共有2个答案

锺离嘉容
2023-03-14

Pair是公共成员的聚合,tuple不是。

您的继承自对正在使用聚合结构化绑定。

Tuple使用Tuple机器。有些元组机制不适用于继承,这是有充分理由的。

所以你需要专门化std::tuple_size

struct s_int_triple : public std::tuple< int, int, int >
{
  using std::tuple< int, int, int >::tuple; // inherit ctor
};
namespace std{
  template<>
  class tuple_size<::s_int_tuple>:public std::integral_constant<std::size_t, 3>{};
  template< std::size_t I >
  class tuple_element<I,::s_int_tuple>:public tuple_element<I, std::tuple<int,int,int>>{};
}

和结构化绑定应该工作。

是的,这太糟糕了。

申屠健
2023-03-14

我从将指令分成两部分开始(使用旧风格的初始化来绕过与std::initailizer_list相关的任何问题,如果有的话):

 s_int_triple s( 0, 0, 0 );
 auto [b1, b2, b3] = s;

编译器错误如下:

main.cpp:27:10: error: cannot decompose class type ‘std::_Tuple_impl<1, int, int>’: its base classes ‘std::_Head_base<2, int, false>’ and ‘std::_Head_base<1, int, false>’ have non-static data members
   27 |     auto [b1, b2, b3] = s;
      |          ^~~~~~~~~~~~

所以我得出结论,构造函数的继承不是问题,问题在于结构化绑定。当你必须查看C的标准模板库时,C是一种糟糕的语言,但在将程序加载到一个像样的IDE后,我发现在std::tuple的gcc 10,2实现中,这个类公开继承了\u tuple\u impl

_Head _M_head_impl;

答对 了这是一个非静态数据成员。

现在是找到结构化绑定分解的适当规则的时候了。我们去https://en.cppreference.com/w/cpp/language/structured_binding然后,直奔“案例3”:

E的每个非静态数据成员必须是E的直接成员或E的同一基类,并且当命名为E.name时,必须在结构化绑定的上下文中格式良好。E可能没有匿名工会成员。标识符的数量必须等于非静态数据成员的数量。

这里我们有了答案:从std::tuple派生的类也继承了其他几个类,其中一些类定义了非静态成员。

这一现象的简单例子:

struct A
{
  int x;
};

struct B: public A
{
  int y;
};

int main()
{
  B obj;
  auto [a, b] = obj;
}

导致

main2.cpp:14:8: error: cannot decompose class type ‘B’: both it and its base class ‘A’ have non-static data members

所以现在我们遇到了一个真正的难题:如果std::tuple继承自不同的类,并且每个类都定义了一个非静态成员,那么为什么我们可以对tuple使用结构化绑定呢?我们回到cppreference链接,看到std::tuple是上述情况3的一个例外。编译器必须以自己特殊的方式处理标准元组和类似的类。换句话说,std::tuple和类似tuple的类由案例2处理,但任何其他类或结构都由限制性更强(而且非常普遍)的案例3处理。

因此,要使程序编译,您必须使类像元组一样,如上面引用的源代码所述。我不知道这是否可能——我想这值得问一个单独的问题。如何做到这一点在@yakk-adam-nevraumont的回答中有所描述

 类似资料:
  • 我不熟悉<code>std::map</code>,最近才开始使用它。 我遇到了其中一个映射的编译问题。 我有一个自定义结构,并试图用该结构类型的对象创建一个< code>CString的映射。 不幸的是,我遇到了问题

  • 我的基本想法是从std::tuple派生我自己的类,以便在里面获得一些helper类型,如下所示: 现在我尝试使用如下代码: 线条 不能用GCC5.2.0编译,而是用GCC6.1.0编译。这有点神秘,因为我记得元组的构造函数确实是显式的。为什么这适用于GCC6.1.0?但这不是我寻找的问题:-) 另一个提示:我遇到问题的代码似乎是用Clang3.5.0编译的。

  • 以下节目 使用当前Clang(12.0.0)编译,但不使用当前GCC(11.0.0 20201028)。使用GCC它会产生错误 根据[dcl.init.list/5]和string_view(char const*)构造函数是constepr这一事实,我假设Clang的行为就在这里。 对吗?

  • 我不知道如何创建以下内容: 我总是得到 /usr/include/c/5.5.0/bits/stl_对。h:139:45:错误:使用已删除的函数'std::atomic::atomic(const std::atomic 我已经试过了 我知道std::atomic是不可复制的,那么你应该如何创建一对呢?难道这不可能吗?

  • 我以前见过这样做,但我不记得如何有效地初始化已知长度的与长度相同的。这里有一个很好的例子: 我已经仔细阅读了这一页关于高级矩阵初始化的内容,但是没有明确解释执行此操作的方法。