【问题标题】:What `auto` in this statement mean? [duplicate]该语句中的“auto”是什么意思? [复制]
【发布时间】:2021-12-18 06:03:30
【问题描述】:
struct Node
{
    int value;
    Node* left;
    Node* right;
    Node(int i = 0) :value(i), left(nullptr), right(nullptr) {}
};

auto left = &Node::left;
auto right = &Node::right;

int main()
{
    Node* root = new Node(0);
    std::cout << typeid(left).name() << std::endl;
    std::cout << left << std::endl;
    std::cout << typeid(root->left).name() << std::endl;
    std::cout << root->left << std::endl;
}

第一个问题:“auto left = &Node::left”是什么意思?类节点不被初始化一个对象,如何使用“Node::left”来赋值?
第二个问题:为什么打印“left”的输出是1?

【问题讨论】:

  • 您可以使用auto 声明一个变量,其中初始化表达式涉及模板、函数指针或成员指针。在上述情况下,您尝试分配结构节点的left 成员的地址。我怀疑它甚至可以正常工作。
  • SO 不适合这个。在每一本“现代”C++ 书籍中都已经解释过了。请看一篇:The Definitive C++ Book Guide and List
  • auto 表示:让编译器推断出实际的类型。这真的可以帮助代码更具可读性和可维护性(可重构)。标准::向量 v; auto it = v.begin() 比 std::vector::iterator it = v.begin(); 更具可读性

标签: c++ auto


【解决方案1】:

auto 允许编译器根据用于初始化变量的值类型推断变量声明的类型。

在这种情况下,auto left 是从 &amp;Node::left 推导出来的,auto right 是从 &amp;Node::right 推导出来的,因为两者都是 Node* Node::* 类型的 pointer-to-data-member(即指向 Node 的指针Node* 类型的成员)。此类指针可以与.* and -&gt;* member access operators 一起使用。

之所以在打印这样的指针时输出1,是因为operator&lt;&lt;没有采用这种指针的重载,但确实有采用bool的重载,并且存在隐式转换从指向bool 的指针定义。默认情况下,operator&lt;&lt;bool 打印为整数01。如果在流上启用boolalpha 标志(例如使用std::boolalpha 流操纵器),则指针将打印为true 而不是1

【讨论】:

  • 谢谢。我知道第二个问题的原因。但是,对于第一个问题,Node struct 没有被实例化,因此内存中不存在 left 成员。如何使用“&”来获取它的地址?
  • struct Node { int value; Node* left; Node* right; Node(int i = 0) :value(i), left(nullptr), right(nullptr) {} }nodes; auto left = &amp;nodes.left; auto left2= &amp;Node::left; 那么left和left2有什么区别。如果我打印左边,我会得到一个地址。而如果我打印left2,我会得到真实的
  • 非常感谢您,先生。看完pointer-to-data-member的链接,我明白了。
【解决方案2】:

auto 是 C++ 11 早期出现的 C++ 功能。

它主要为你做两件事:

  1. 更短的语法。谁不喜欢写较短的类型,对吧?虽然有时并不实用,这真的取决于人来决定。例如,这个:
std::vector<unsigned int>::size_type i = 0;

...被简化为:

auto i = 0;

这就是我所说的实用性。有时,有很多 C++ 类型、大量数字、uint32_t、int 等。有时编译器为您推导出的内容并不准确。或者具体的,就此而言。

  1. 为您获取类型,就像 1-2-3 一样简单(或不)。那么,如果我们有一个类型不明确的变量怎么办,例如,我们有这个函数:
void do_work(auto &ref);

...我们不知道类型,是吗?您可以使用模板,但这是另一种方法,真的。这会为您获取类型,然后告诉您调用该函数时它是什么。


将 auto 添加到任何变量或处理数据类型的任何内容基本上都会告诉编译器:“嘿!根据它的初始化程序获取此变量的类型”,所以如果初始化程序是向量、双端队列或其他东西, C++ 将使用它。

有趣的事实:根据牛津语言,“汽车”也代表“自己的”。 (所以也许编译器真的会做出决定)。

很好的阅读资源!:https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP11.md#auto.

【讨论】:

【解决方案3】:

您可以使用C++ Insights 更好地了解编译器在做什么:

// [...]

auto left = &Node::left;
auto right = &Node::right;

// [...]

变成:

// [...]

using MemberVarPtr_11 = Node *Node::*;
MemberVarPtr_11 left = &Node::left;

using MemberVarPtr_12 = Node *Node::*;
MemberVarPtr_12 right = &Node::right;

// [...]

换句话说,变量left的类型为Node *Node::*。 这是一个指向Node 成员的指针,其类型为Node*

这没什么用,除非您想花哨或处理某些框架或库的内部结构。例如,该语法可用于在运行时进行反射(可以通过名称访问成员):

// Magic, implemented by framework
#define REGISTER_PROPERTY(name, member_pointer)

struct A {
    A() {
        // You inform the framework about available members.
        REGISTER_PROPERTY("foo", &A::foo);
    }

    int foo;
};

在 C++ 中的Godot you can add custom modules 中,脚本语言可以使用一些反射机制与之交互。要完成这项工作,您需要声明所有可访问的方法:

void Summator::_bind_methods() {
    ClassDB::bind_method(D_METHOD("add", "value"), &Summator::add);
    ClassDB::bind_method(D_METHOD("reset"), &Summator::reset);
    ClassDB::bind_method(D_METHOD("get_total"), &Summator::get_total);
}

这是使用成员函数指针,但成员也可以这样做。

【讨论】:

    猜你喜欢
    • 2011-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-29
    相关资源
    最近更新 更多