函数指针和返回类型转换

时间:2012-06-02 23:41:34

标签: c++ templates c++11 type-conversion function-pointers

假设我有一个执行某些副作用的函数然后返回一个答案:

int foo()
{
    perform_some_side_effect();
    return 42;
}

我想将foo绑定到函数指针,但我对答案不感兴趣,只是副作用:

void (*bar)() = foo;

但是,这似乎是一个类型错误:

error: invalid conversion from ‘int (*)()’ to ‘void (*)()’

该错误背后的理由是什么?为什么类型系统不允许我忽略答案?


在旁注中,如果我将函数指针包装在std::function

中,它就可以工作
std::function<void()> baz = foo;

std::function(显然)如何设法绕过类型系统中的这种限制?

4 个答案:

答案 0 :(得分:9)

  

该错误背后的理由是什么?为什么类型系统不允许我忽略答案?

原因是类型不同,并且在调用地(通过函数指针)生成的代码是不同的。考虑一个调用约定,其中所有参数都写入堆栈,返回值的空间也保留在堆栈中。如果调用通过void (*)(),则堆栈中不会为返回值保留任何空间,但函数(不知道如何调用它)仍会将42写入到的位置呼叫者应该预留空间。

  

std :: function(显然)如何设法绕过类型系统中的这种限制?

没有。它创建了一个函数对象,它将调用包装到实际函数中。它将包含如下成员:

void operator()() const {
   foo();
}

现在,当编译器处理对foo的调用时,它知道它必须做什么来调用一个返回int的函数,它将根据召集会议。因为模板没有返回,所以它只会忽略实际返回的值。

答案 1 :(得分:1)

std::function只需要与源兼容 - 也就是说,它可以生成一个新类,它会生成忽略结果的新校准代码。函数指针必须是二进制兼容的,不能完成那项工作 - void(*)()int(*)()指向完全相同的代码。

答案 2 :(得分:1)

您可以考虑std::function<>为您的特定情况做这件事:

void __func_void()
{
    foo();
}

它实际上比这复杂一点,但重点是它与 type-erasure 一起生成模板代码而不关心具体细节。

答案 3 :(得分:1)

除了其他人所说的之外,调用者还需要返回类型来知道它应该对结果调用什么析构函数(返回值可能是临时的)。


不幸的是,它不像

那么容易
auto (*bar)() = foo; 

虽然GCC和Clang接受了这一点。我需要重新检查规范,看看这是否真的正确。

更新:规范说

  

auto type-specifier 表示声明的变量的类型应从其初始化程序推导出,或者函数声明符应包含 trailing-return-type。

当快速读取时,这可能会产生误导,但这是由GCC实现的,而clang仅适用于顶层声明符。在我们的例子中,这是一个指针声明器。嵌套在其中的声明符是函数声明符。因此,只需将auto替换为void,然后编译器就会为您推断出类型。


顺便说一句,你总是可以手动完成这项工作,但要让它工作需要一些技巧

template<typename FunctionType>
struct Params; 

template<typename ...Params>
struct Params<void(Params...)> {
  template<typename T>
  using Identity = T;

  template<typename R>
  static Identity<R(Params...)> *get(R f(Params...)) {
    return f;
  }
};

// now it's easy
auto bar = Params<void()>::get(foo);