如何在模板化的函数指针声明中读取这么多的星星和括号?

时间:2017-12-15 15:40:51

标签: c++ c++11 function-pointers declaration

来自Introduction to the C++11 feature: trailing return types

文章声称

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;
}

我不理解第二个代码示例中的复杂功能。我应该在一开始看哪里?我猜是foo。但foo旁边的统计数据将foo定义为指针...... 基于第一个代码示例,我将把这个片段转换为

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

所以foo是一个返回0的函数,但是返回类型很棘手:它的返回类型是函数指针,它的返回类型又是一个返回类型为tmp<int>的函数指针。

4 个答案:

答案 0 :(得分:24)

  

一开始我应该在哪里看?

老实说,您应该只看https://cdecl.org/,其中int (*(*foo())())();描述为:

  

声明foo为函数返回指向函数的指针返回指向函数返回int的指针

然后意识到这是C ++ 11,我们有一个很好的语法来声明函数指针别名:

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

今天真的没有理由写这样的声明。

答案 1 :(得分:16)

作为@Vittorio答案的补充,有Clockwise Spiral Rule来帮助我们decypher复杂类型:

从未知元素开始,以螺旋/顺时针方向移动;当遇到以下元素时,用相应的英语语句替换它们:

  • [X][]

      

    数组X大小...或数组未定义大小...

  • (type1, type2)

      

    函数传递type1和type2返回...

  • *

      

    指针......

以螺旋/顺时针方向继续这样做,直到所有标记都被覆盖。始终先解决括号内的任何内容!

下面:

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

foo是一个返回指向函数的指针的函数,该函数返回一个返回temp<int>的函数的指针。

现在,@ UKmonkey刚刚重命名了这个规则 C ++ Guru Snail Rule 或简称CGSR:

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

答案 2 :(得分:5)

cdecl是一个有用的在线工具,用于揭开复杂的C声明的神秘面纱。

插入int (*(*foo())())()会返回:

  

声明foo为函数返回指向函数的指针返回指向函数返回int的指针

我已将tmp<int>替换为int,因为该工具不支持模板。

答案 3 :(得分:5)

正确格式化代码可能有助于您理解:

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;
}

template class部分保持不变,所以我不打算详细说明。让我们看看函数foo

在第一个代码中,foo()的返回值是auto(*)() -> tmp<int>(*)(),它是指向返回另一个指针的函数的指针,该指针指向返回tmp<int>的函数。

因为您总是可以定义一个函数指针,如:

base_type_t (*pointer_name)(parameter_list);

使用函数(即pointer_name)递归func_name()可以声明一个返回值为指针的函数:

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

所以现在(*func_name())(parameter_list)可以提供另一个功能。让我们把它放回到函数指针定义语法中:

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

清除参数列表(它们为空)并用正确的类型替换标识符:

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

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

正如其他人已经建议的那样,https://cdecl.org/是一个很好的代码分析器,虽然它可能会给你另一个不太容易理解的句子。