【问题标题】:Declaring a using declaration before using it in other using declarations在其他 using 声明中使用之前声明 using 声明
【发布时间】:2020-09-05 02:28:28
【问题描述】:

是否可以在使用前声明 using 声明?

我想做这样的事情:

using V = std::vector< V >;

或喜欢:

using X, Y; // declaring X and Y
using Node = std::variant< X, Y >;
using X = std::unordered_vector< Node >;
using Y = std::vector< Node >;

有可能吗?


编辑:cmets 关注V 我定义的比我的意思更多。

我不需要那种类型,但这是我能想到的最简单的例子来说明问题。第二个例子,XYNode 更接近我的需要。另一个类似的例子可能是:using MyList = std::pair&lt;Data, MyList*&gt;

我作为示例提出的这些类型是有意义的并且应该能够存在的类型。我可以通过定义我自己的结构来定义VNodeMyList 等,例如:struct V{ V* start; V* end; /*all the other methods here*/ };... 但是因为在std 中已经定义了很多类型并且具有大量定义明确的语义(构造函数、运算符、迭代器的东西、专门为它们设计的特征类型等),我不想重新定义相同的类型。

是否可以使用 using-declarations 创建“递归”(尤其是间接递归,如 NodeMyList 示例)类型?

【问题讨论】:

  • 我觉得这可能是一个 XY 问题。为什么要这样做?
  • std::vector&lt; V &gt; 其中V 本身就是std::vector&lt; V &gt; 没有意义——它一直是海龟。你打算用多少对方括号来访问这个野兽的一个元素,那个元素的类型是什么?
  • 所以你想让V由一个元素序列组成,每个元素包含一个元素序列,每个元素都包含一个元素序列,每个元素都包含一个元素序列,每个其中保存了一个元素序列,每个元素保存一个元素序列,每个元素保存一个元素序列,每个元素保存一个元素序列,每个元素保存一个元素序列,每个元素保存一个序列元素,每个元素保存一个元素序列,每个元素保存一个元素序列,每个元素保存一个元素序列,每个元素......?
  • 您已经可以在 C++ 中执行此操作。它被称为泛型类型。在 C++ 20 中,我们还可以访问进一步巩固类型及其用法的约束和概念
  • 为什么每个人都如此关注“递归”向量?我认为选择我能想到的最小的例子会让我的问题更清楚。我并不是要开始对这种类型的含义进行哲学讨论。

标签: c++ declaration using


【解决方案1】:

using 的方法不起作用,因为这只是为现有类型引入了一个新名称。 using V = std::vector&lt;V&gt;;,如果它被允许的话,当它试图确定 V 是什么类型时,它会导致编译器中的无限递归。

一种可行的方法是从V 类型继承std::vector&lt;V&gt;。 (using vector::vector;vector 构造函数引入V。)

struct V : std::vector<V> { using vector::vector; };

这打破了递归。 std::vector&lt;V&gt; 有效,因为 V 是它自己的类型。 V 继承 std::vector&lt;V&gt; 的事实与这一点无关。

请注意,当基类型未声明虚拟析构函数时,您不能通过指向基类型的指针delete 类型。这会导致未定义的行为:

std::vector<V> * ptr = new V(); // Permitted, implicit pointer upcast
delete ptr;                     // Undefined behavior

意外切割基础对象也很容易,尽管在这种情况下这样做的后果可能无关紧要。

同样的方法也适用于您的其他示例:

class Node;

using X = std::set<Node>;
using Y = std::vector<Node>;

struct Node : std::variant<X, Y> { using variant::variant; };

【讨论】:

  • 感谢您的想法和指出删除超类的“危险”未定义行为。我怀疑在实践中它应该适用于每个编译器,在我的情况下,我认为我不会破坏向上转换的类型。
  • @Helloer 它可能并不适用于每个编译器。如果子对象与最派生对象不在完全相同的内存位置,delete 将无法找到分配元数据以正确解除分配。
  • 当然,但是如果任何编译器将我的类型和超类放在不同的地址,我会感到惊讶:单一继承,没有虚拟,没有数据成员......
  • @Helloer 很可能,是的。请记住,“在此编译器上运行良好”是另一种说法,即“可能会停止在未来的编译器版本上工作”。
【解决方案2】:

using 不会创建新类型。它只为现有类型引入别名。您不能为不存在的类型引入别名,这是您的两个示例都试图做的。

例如,V 作为别名的类型的全名是什么?这是std::vector&lt;std::vector&lt;std::vector&lt;std::vector&lt;...&gt;&gt;&gt;&gt;。那不是一个存在的类型,所以你不能为它创建别名。

【讨论】:

    猜你喜欢
    • 2011-07-06
    • 1970-01-01
    • 2013-02-22
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    • 2021-08-02
    • 2011-08-06
    • 1970-01-01
    相关资源
    最近更新 更多