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;
}
First Question: What does "auto left = &Node::left" means ? The Class Node donot be initialized an object, how can use "Node::left" to assign left ?
Second Question: Why the output of printing "left" is 1 ?
CodePudding user response:
auto
allows the compiler to deduce the type of a variable declaration based of what type of value is used to initialize it.
In this case, auto left
is deduced from &Node::left
, and auto right
is deduced from &Node::right
, as both being the pointer-to-data-member type Node* Node::*
(ie, a pointer to a Node
member which is of type Node*
).
The reason that 1
is output when printing such a pointer is because operator<<
does not have an overload that takes such a pointer, but it does have one that takes a bool
, and there is an implicit conversion defined from a pointer to a bool
. By default, a bool
is printed by operator<<
as an integer 0
or 1
. If you enable the boolalpha
flag on the stream (such as with the std::boolalpha
stream manipulator), the pointer will be printed as true
instead of 1
.
CodePudding user response:
auto
is a C feature that arrived in the dawn of C 11.
It does mostly 2 things for you:
- Shorter syntax. Who doesn't like writing shorter types, right? Although sometimes it's not practical, and it's really up to the person to decide. So for example, this:
std::vector<unsigned int>::size_type i = 0;
...gets reduced to:
auto i = 0;
And that's what I was talking about, regarding practicality. Sometimes, there are lots of C types, tons of numbers, uint32_t, int, etc. Sometimes what the compiler deduces for you is not accurate. Or specific, for that matter.
- Get the type for you, as easy as 1-2-3 (or not). So what if we had a variable with a type that's ambiguous, say for example, we had this function:
void do_work(auto &ref);
...we don't know the type, yeah? You could use templates, but this is another way to do it, really. That gets the type for you, then tells you what it is when the function is called.
Adding auto to any variable or whatever that deals with data types basically tells the compiler: "hey you! Get the type of this variable based on it's initializer", so if the initializer was a vector, deque, or something else, C will use that.
Fun fact: "Auto", according to Oxford Languages, also stands for "one's own." (so maybe the compiler really makes decisions).
Good resource for reading!: https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP11.md#auto.
CodePudding user response:
You can use C Insights to get a better understanding what the compiler is doing:
// [...]
auto left = &Node::left;
auto right = &Node::right;
// [...]
becomes:
// [...]
using MemberVarPtr_11 = Node *Node::*;
MemberVarPtr_11 left = &Node::left;
using MemberVarPtr_12 = Node *Node::*;
MemberVarPtr_12 right = &Node::right;
// [...]
In other words, the variable left has type Node *Node::*
.
This is a pointer to a member of Node
hat has type Node*
.
This is not useful unless you are trying to be fancy or working on the internals of some framework or library. That syntax can be used for example to do reflection at runtime (making it possible to access a member by name):
// 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;
};
In Godot you can add custom modules in C and the scripting language can then interact with it using some reflection mechanism. To make this work you need to declare all the accessible methods:
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);
}
That's using member function pointers but the same can be done with members.