C ++函数返回函数

时间:2015-07-13 15:18:32

标签: c++ c++11 standards language-lawyer

标准中哪些函数不允许返回函数?我知道它们在概念上是荒谬的,但在我看来,语法会允许它们。根据这个网页," noptr-declarator [is] any valid declarator"其中包括函数的声明者:

#!/usr/local/bin/perl
use strict;
use warnings;

my %M_hash;
my %F_hash; 
open (my $input, "<", 'ssbn1898.txt');
while ( <$input> ) {

    chomp;

   my ( $name, $id ) = split ( /,/ ); 


    if ( $id eq "M" ) {

        push ( %M_hash, $name );
    }
    else {
        push ( %F_hash, $name ); 
    }
}
close ( $input );
print 'M: ' . join("\t", %M_hash) . "\n"; 
print 'F: ' . join("\t", %F_hash) . "\n"; 

关于语法。

在我看来,[dcl.decl]中详细说明的语法允许

int f()();

可以解释为函数 int f(char)(double) ,它接受 f 并返回一个具有相同签名的函数 char

int g(double)

粗略地讲,之后 1-> 2,2 = 4,4-> 6,4-> 6 你应该有     ptr-operator ptr-operator ptr-operator 然后,对第一个声明者使用4-> 5,5 = 7,7-> 8;对于第二和第三声明符,使用4-> 5,5 = 7,7-> 9。

7 个答案:

答案 0 :(得分:54)

来自[dcl.fct],非常明确地说:

  

函数不应具有类型数组或函数的返回类型,尽管它们的返回类型可能为   键入指针或对此类事物的引用。虽然可以有数组,但不应有任何函数数组   指向函数的指针。

使用C ++ 11,您可能只想要:

std::function<int()> f();
std::function<int(double)> f(char);

关于C ++语法存在一些混淆。可以根据语法解析语句int f(char)(double); 。这是一个解析树:

grammar

此外,基于[dcl.fct] / 1:

,这样的解析甚至是有意义的
  

在声明T D中,D的格式为   D1 parameter-declaration-clause cv-qualifier-seq opt
   ref-qualifier opt exception-specification opt attribute-specifier-seq 选择
  并且声明T D1中包含的 declarator-id 的类型是“ derived-declarator-type-list T”,   D declarator-id 的类型是( parameter-declaration-clause derived-declarator-type-list 函数) cv-qualifier-seq opt    ref-qualifier opt 返回T“。

在此示例中T == intD == f(char)(double)D1 == f(char)T D1int f(char))中 declarator-id 的类型是“(char)return int”的函数。所以 derived-declarator-type-list 是“(char)返回的函数”。因此,f的类型将被读作“(char)返回函数的函数(double)返回int。”

这最终无关紧要,因为这是一个明确禁止的声明者形式。但不是语法。

答案 1 :(得分:7)

使用C++11(但不是以前的C ++版本),您不仅可以返回类C函数指针,还可以返回C ++ closures,尤其是anonymous functions。另请参阅std::function

标准不允许( 语义 ,而不是语法 - 所以 > ;请参阅Barry's answer以获取引文)返回函数(并且还禁止sizeof函数!)但允许返回函数指针

BTW,我认为你不能归还整个功能。那是什么意思?你会如何实现?实际上,函数是一些代码块,它的名称(就像数组一样)是指向函数机器代码开头的指针。

一个很好的技巧可能是在运行时构建(使用C ++标准的外部机制)函数(然后处理它的函数指针)。一些外部库可能允许:您可以使用JIT库(例如asmjitgccjitLLVM ...)或者只是生成C ++代码,然后编译并dlopen &安培; dlsym它在POSIX系统上等等。

PS。你可能正确地理解C ++ 11 语法(标准中的EBNF规则)不允许返回函数。这是一个用简单的英语表达的语义规则,它不允许(任何语法规则)。我的意思是单独的EBNF会允许:

 // semantically wrong... but perhaps not syntactically
 typedef int sigfun_T(std::string);
 sigfun_T foobar(int);

由于semantics原因(不是因为EBNF规则),编译器正确地拒绝上述代码。实际上,symbol table对C ++编译器很重要(并且不是语法或无上下文语法)。

关于C ++的可悲事实是(由于遗留原因)它的语法(单独)非常模糊。因此,C ++ 11难以阅读(对于人类而言),难以编写(对于开发人员而言),难以解析(对于编译器而言),....

答案 2 :(得分:0)

C的形式语法实际上不允许返回函数,但是,总是可以返回一个函数指针,出于所有意图和目的,它似乎就像你想要做的那样:

  int (*getFunc())(int, int) { … }

我很着迷说,语法是对这种功能缺乏支持的更基本的解释。标准的规则是后一个问题。

如果语法没有提供某种方法来完成某些事情,那么对于任何给定的无上下文语言,我认为语义或标准所说的并不重要。

答案 3 :(得分:0)

除非您返回一个指针或对函数的引用,否则替代方法是返回函数的副本。现在,考虑一下函数的副本是什么样的,行为就像,表现得像。首先,这将是一个字节数组,也是不允许的,其中第二个字节将是一段代码的等价物,字面上返回一段代码....几乎所有的启发式病毒扫描程序会认为是病毒,因为也无法验证运行时系统返回的代码的可行性,甚至无法在编译时验证。即使你可以返回一个数组,你会如何返回一个函数?返回数组(可能是堆栈上的副本)的主要问题是大小未知,因此无法将其从堆栈中删除,并且函数存在同样的困境(其中数组将是机器语言二进制代码)。另外,如果你确实以这种方式返回一个函数,你会如何转身并调用该函数?

总而言之,将函数而不是函数返回函数的概念失败了,因为它的概念是放置(复制)到堆栈上的未知大小的机器代码数组。它不是C或C ++设计允许的东西,而且现在有了转向并调用该函数的方法,特别是我想要传递参数。

我希望这是有道理的

答案 4 :(得分:0)

从某种意义上说,function_pointer是其自身的功能,
而“尾随返回类型”在c ++ 11中是一件好事且隐藏的东西,我宁愿这样写:

#include<iostream>



auto return0(void)->int
{return 0;}
auto returnf(void)->decltype(&return0)
{return &return0;}



/*Edit: or...
auto returnf(void)->auto (*)(void)->int
{return &return0;}
//that star means function pointer
*/




auto main(void)->int
{
    std::cout<<returnf()();
    return 0;
}

(如果类型不匹配,请尝试使用地址运算符“&”)
看,很有道理。

但缺点是功能开头的“自动”内容,
那是不可移动的,没有类型可以匹配它(即使lambda也可以匹配模板类型std :: function <>) 但是如果您愿意,宏可以为您做魔术(有时是诅咒)

#define func auto

答案 5 :(得分:0)

using namespace std;

auto hello()
{
    char name[] = "asdf";
    return [&]() -> void
    {
        printf("%s\n", name);
    };
}

int main()
{
    hello()();
    auto r = hello();
    r();
    return 0;
}

答案 6 :(得分:-1)

实际上在C中一个人无法通过或返回功能。只能传递/返回函数的指针/地址,这在概念上非常接近。老实说,由于可能会遗漏&*函数指针,因此不应该关注函数或指针是否传递(除非它包含任何静态数据)。这是简单的函数指针声明:

void (*foo)();

foo是函数返回void并且不带参数的指针。

在C ++中它并没有太大的不同。对于所有可调用的创建,仍然可以使用C风格的函数指针或新的有用的std::function对象。 C ++还添加了lambda expressions,它们是内联函数,它们在某种程度上类似于函数式语言中的闭包。它允许您不要将所有传递的函数全局化。

最后 - 返回函数指针或std::function可能看起来很荒谬,但实际上并非如此。例如,状态机模式(在大多数情况下)基于返回指向函数处理下一状态或可调用的下一状态对象的指针。