How to read so many stars and parentheses in a tem

2019-01-22 22:47发布

问题:

This question already has an answer here:

  • Complex C declaration 7 answers

From Introduction to the C++11 feature: trailing return types

The article claims

template <class T> class tmp {
public:
    int i;
};

auto foo()->auto(*)()->tmp<int>(*)(){
    return 0;
}

is equivalent to

template <class T> class tmp{
public:
    int i;
};

tmp<int> (*(*foo())())() {
    return 0;
}

I don't understand the complex function in the second code example. Where should I look at in the beginning? I guess is foo. But the stat right next to foo is going to define foo as a pointer... Based on the first code example, I will convert the piece as

tmp<int> (*)() (*)() foo(){ return 0;}

So foo is a function, which returns 0, but the return type is tricky: its return type is functional pointer whose return type is again a function pointer whose return type is tmp<int>.

回答1:

In complement with @Vittorio answer, there is the Clockwise Spiral Rule to help us decypher complex types:

Starting with the unknown element, move in a spiral/clockwise direction; when encountering the following elements replace them with the corresponding English statements:

  • [X] or []

    Array X size of... or Array undefined size of...

  • (type1, type2)

    Function passing type1 and type2 returning...

  • *

    pointer(s) to...

Keep doing this in a spiral/clockwise direction until all tokens have been covered. Always resolve anything in parenthesis first!


Here:

           +-----------+
           | +------+  |
           | | >-v  |  |
temp<int> (*(*foo())())()
        |  | ^---+  |  |
        |  ^--------+  |
        +--------------+

foo is a function returning a pointer to a function returning a pointer to a function returning a temp<int>.


And now, @UKmonkey just renamed this rule The C++ Guru Snail Rule or CGSR for short:

 / /
 L_L_
/    \
|00  |       _______
|_/  |      /  ___  \
|    |     /  /   \  \
|    |_____\  \_  /  /
 \          \____/  /_____
  \ _______________/______\.............................


回答2:

Where should I look at in the beginning?

Honestly, you should just look at https://cdecl.org/, which describes int (*(*foo())())(); as:

declare foo as function returning pointer to function returning pointer to function returning int

And then realize that this is C++11, and we have a really nice syntax for declaring function pointer aliases:

using A = int(*)(); // pointer to function returning int
using B = A(*)();  // pointer to function returning pointer to function returning int

B foo(); // function returning pointer to function returning pointer to function returning int

There's really no reason to write declarations like that today.



回答3:

cdecl is an useful online tool to demystify complicated C declarations.

Inserting int (*(*foo())())() returns:

declare foo as function returning pointer to function returning pointer to function returning int

I've replaced tmp<int> with int as the tool does not support templates.



回答4:

Correctly formatting the code may help you comprehend:

template <class T>
class tmp {
    public:
    int i;
};

auto foo() -> auto(*)() -> tmp<int>(*)() {
    return 0;
}
template <class T>
class tmp{
    public:
    int i;
};

tmp<int> (*
    ( *foo() )()
    )() {
    return 0;
}

The template class part remains the same so I'm not going to elaborate on it. Let's see the function foo.

In the first code, the return value of foo() is auto(*)() -> tmp<int>(*)(), which is a pointer to a function returning another pointer, which points to a function returning tmp<int>.

As you can always define a function pointer like:

base_type_t (*pointer_name)(parameter_list);

Recursing the pointer_name with a function (i.e. func_name()) can declare a function whose return value is such a pointer:

base_type_t (*func_name())(parameter_list);
              ~~~~~~~~~~~

So now (*func_name())(parameter_list) can serve another function. Let's put it back into the function pointer definition syntax:

base_type_t (*(*func_name())(parameter_list))(parameter_list);
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Clear the parameter list (they're empty) and replace the identifiers with the correct types gives:

base_type_t (*(*func_name())(parameter_list))(parameter_list);
tmp<int>    (*(*   foo   ())( /* Empty */  ))( /* Empty */  );

// Turns to
tmp<int> (*(*foo())())();

As others have already suggested, https://cdecl.org/ is a good code analyzer, though it may give you another sentence which is not quite easy to understand.