C ++编译器允许循环定义吗?

时间:2019-01-07 10:57:44

标签: c++ c++14

在为树写一些代码时犯了一个错误。我已经精简了这个示例,所以它只是一个线性树。

基本上,在main()函数中,我想将Node附加到我的树上,但是我没有将其附加到“ tree.root”上,而是仅将其附加到“ root”上。但是,令我惊讶的是,不仅可以很好地进行编译,而且我能够在节点上调用方法。仅在我尝试访问“值”成员变量时出错。

我想我的主要问题是,为什么编译器没有捕获此错误?

std::shared_ptr<Node> root = tree.AddLeaf(12, root);

由于RHS上的“根”是一个完全未声明的变量。另外,出于好奇,如果编译器允许它们通过,那么循环定义是否具有实际用例?这是其余的代码:

#include <iostream>
#include <memory>

struct Node
{
    int value;
    std::shared_ptr<Node> child;

    Node(int value)
    : value {value}, child {nullptr} {}

    int SubtreeDepth()
    {
        int current_depth = 1;
        if(child != nullptr) return current_depth + child->SubtreeDepth();
        return current_depth;
    }
};

struct Tree
{
    std::shared_ptr<Node> root;

    std::shared_ptr<Node> AddLeaf(int value, std::shared_ptr<Node>& ptr)
    {
        if(ptr == nullptr)
        {
            ptr = std::move(std::make_shared<Node>(value));
            return ptr;
        }
        else
        {
            std::shared_ptr<Node> newLeaf = std::make_shared<Node>(value);
            ptr->child = std::move(newLeaf);
            return ptr->child;
        }
    }
};


int main(int argc, char * argv[])
{

    Tree tree;
    std::shared_ptr<Node> root = tree.AddLeaf(12, root);
    std::shared_ptr<Node> child = tree.AddLeaf(16, root);

    std::cout << "root->SubtreeDepth() = " << root->SubtreeDepth() << std::endl; 
    std::cout << "child->SubtreeDepth() = " << child->SubtreeDepth() << std::endl; 

    return 0;
}

输出:

root->SubtreeDepth() = 2
child->SubtreeDepth() = 1

2 个答案:

答案 0 :(得分:23)

这是C ++中定义的不幸的副作用,声明和定义是作为单独的步骤完成的。因为变量首先被声明 ,所以可以在其自身的初始化中使用它们:

std::shared_ptr<Node> root = tree.AddLeaf(12, root);
^^^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^
Declaration of the variable  Initialization clause of variable

一旦声明了变量,就可以在初始化中使用它的完整定义。

如果使用第二个参数的数据,则会导致AddLeaf中的undefined behavior,因为该变量未初始化。

答案 1 :(得分:13)

  

由于RHS上的“根”是一个完全未声明的变量。

它不是未声明的。它是由同一条语句声明的。但是,root 在调用AddLeaf(root)时未初始化,因此,当在函数中使用对象的值(与null等相比)时,行为未定义。

是的,允许在其自己的声明中使用变量,但不允许使用其值。您几乎可以做的就是获取地址或创建引用,或者仅处理子表达式类型的表达式,例如sizeofalignof

是的,有一些用例,尽管它们可能很少见。例如,您可能想表示一个图,并且您可能有一个节点的构造函数,该结构以指向链接节点的指针作为参数,并且您可能希望能够表示与其自身链接的节点。因此,您可以编写Node n(&n)。我不会争辩说这对于图形API来说是否是一个好的设计。