4

什么类型的 T 使以下代码可编译?

T f(){ return &f; }

我更喜欢 C 答案,但我将问题标记为 C 和 C++,以防只有使用模板的答案。

4

8 回答 8

9

我希望这不是作弊(仅限 C++):

class T {
private:
    T (*_func)();

public:
    T(T (*func)()) : _func(func) {}

    T operator()() {
        return *this;
    }
};

T f() { return &f; }

int main() {
    f()()()()()()()();
}
于 2010-06-15T20:23:31.090 回答
9

有趣的问题,但我猜没有可接受的 C 解决方案。

为什么这是不可能的 C ?(这里有一些猜测)

返回 T 的函数的类型是:

T (*)(void) ;

当然,它期望定义 T ......但是,由于 T 是函数本身的类型,因此存在循环依赖关系。

对于结构 T,我们可以有:

struct T ;               /* forward declaration */
typedef T * (*f)(void) ; /* f is a function returning a pointer to T */

下一个符号不是很方便吗?

function T ; /* fictional function forward-declaration.
                It won't compile, of course */
T T(void) ;  /* function declaration */

但是由于无法前向声明函数,因此无法使用您在问题中编写的构造。

我不是编译器律师,但我相信这种循环依赖的创建只是因为 typedef 表示法,而不是因为 C/C++ 限制。毕竟,函数指针(我在这里谈论的是函数,而不是对象方法)都具有相同的大小(以相同的方式结构或类指针都具有相同的大小)。

研究 C++ 解决方案

至于 C++ 解决方案,以前的答案给出了很好的答案(我正在考虑zildjohn01 的答案,here)。

有趣的一点是,它们都是基于结构和类可以被前向声明的事实(并且在它们的声明体中被认为是前向声明的):

#include <iostream>

class MyFunctor
{
   typedef MyFunctor (*myFunctionPointer)() ;
   myFunctionPointer m_f ;
   public :
      MyFunctor(myFunctionPointer p_f) : m_f(p_f) {}
      MyFunctor operator () ()
      {
         m_f() ;
         return *this ;
      }
} ;

MyFunctor foo()      {
   std::cout << "foo() was called !" << std::endl ;
   return &foo ;
}

MyFunctor barbar()   {
   std::cout << "barbar() was called !" << std::endl ;
   return &barbar ;
}

int main(int argc, char* argv[])
{
   foo()() ;
   barbar()()()()() ;
   return 0 ;
}

哪个输出:

foo() was called !
foo() was called !
barbar() was called !
barbar() was called !
barbar() was called !
barbar() was called !
barbar() was called !

从 C++ 解决方案获得 C 解决方案的灵感

难道我们不能在 C 中使用类似的方式来获得可比较的结果吗?

不知何故,是的,但结果并不像 C++ 解决方案那样性感:

#include <stdio.h>

struct MyFuncWrapper ;

typedef struct MyFuncWrapper (*myFuncPtr) () ;

struct MyFuncWrapper { myFuncPtr f ; } ;

struct MyFuncWrapper foo()
{
   printf("foo() was called!\n") ;

   /* Wrapping the function */
   struct MyFuncWrapper w = { &foo } ; return w ;
}

struct MyFuncWrapper barbar()
{
   printf("barbar() was called!\n") ;

   /* Wrapping the function */
   struct MyFuncWrapper w = { &barbar } ; return w ;
}

int main()
{
   foo().f().f().f().f() ;
   barbar().f().f() ;

   return 0 ;
}

哪个输出:

foo() was called!
foo() was called!
foo() was called!
foo() was called!
foo() was called!
barbar() was called!
barbar() was called!
barbar() was called!

结论

您会注意到 C++ 代码在语义上与 C 代码非常相似:每个源将使用一个结构作为指向函数指针的容器,然后,如果需要,使用包含的指针再次调用它。当然,C++ 解决方案使用 operator() 重载,使符号私有,并使用特定的构造函数作为语法糖。

(这就是我找到 C 解决方案的方式:尝试“手动”重现 C++ 解决方案)

我不相信我们可以通过使用宏来改进 C 解决方案的语法糖,所以我们坚持使用这个 C 解决方案,我觉得它远非令人印象深刻,但在我找到它的时间里仍然很有趣。

毕竟,寻找奇怪问题的解决方案是一种可靠的学习方式……

:-)

于 2010-06-15T22:36:28.820 回答
4

正如C 常见问题解答问题 1.22所解释的,这在 C 中是不可能的。解决方法包括将函数指针包装在 a 中struct并返回它或返回另一个(任意)函数指针类型,这是可能的,因为函数指针类型之间的转换保证是无损的。

于 2010-06-15T22:21:08.077 回答
2

有趣的是,我最近一直在考虑这个确切的事情(只是我希望函数将指针指向自身而不是返回它)。

对于 C++,您已经从 zildjohn01 获得了答案。

如果我们坚持使用标准 C,则没有任何解决方案可以完全按照编写的方式进行编译。您可以使用显式强制转换来完成它 -void*不起作用,因为函数指针到数据指针的转换不符合标准,但您可以使用任何其他函数指针类型(例如void(*)()可以) - 标准明确允许从任何函数指针类型转换为任何其他函数类型并返回,并保证您将获得原始值。

于 2010-06-15T20:48:10.213 回答
1

这个问题的答案将付出不亚于你永恒灵魂的代价。

于 2010-06-15T20:41:54.417 回答
0

在 C++ 中,简单:

struct my_function {
    my_function& operator()() {
        return *this;
    }
};

在 C 语言中,您必须强制转换 void*s,这将在任何合理的实现中起作用,但我相信在技术上是未定义的。

如果两种语言都允许简单的递归类型,那就太好了。唉,他们没有。

于 2010-06-15T20:24:54.330 回答
0

没有类型使其兼容,因为您将在 typedef 中进行无限递归。

但是,可以使用 DrPizza 之类的技巧来模拟它。但是,您永远无法真正做到这一点。

由 auto/decltype 的奇迹支持:

auto f() -> decltype(&f) { return &f; } = a shitload of errors.
于 2010-06-15T20:29:44.107 回答
0

C 编译器后端没有理由不能处理这个问题,而幸运的前端(除了解析器和符号解析传递之外)也可以。但是定义 C 的方式使得将数据获取到编译器的后续位是不可能的。OTOH 是一个 C 编译器,它通过引用实现 typdef,并且在解析它们使用的符号之前不符合将 typedef 填充到符号表中的程度,实际上会很好地吞下它。

于 2010-07-01T00:57:23.750 回答