Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 函数指针和返回类型转换_C++_Templates_C++11_Type Conversion_Function Pointers - Fatal编程技术网

C++ 函数指针和返回类型转换

C++ 函数指针和返回类型转换,c++,templates,c++11,type-conversion,function-pointers,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 (*)()’ 这一错误背后的理由是什么?为什么类型系统不允许我忽略答案 另

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

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::函数baz=foo;

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

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

您可以考虑
std::function
针对特定情况执行此操作:

void __func_void()
{
    foo();
}
它实际上比这要复杂一点,但关键是它与类型擦除一起生成模板代码,而不关心细节

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

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

std::function(显然)是如何在类型系统中绕过这个限制的

事实并非如此。它创建一个函数对象,将调用包装到实际函数。它将包含一个成员,如:

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

现在,当编译器处理对
foo
的调用时,它知道要调用一个返回
int
的函数必须做什么,并且它将根据调用约定这样做。因为模板不返回,它只会忽略实际返回的值。

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


不幸的是,这不是那么容易

尽管GCC和Clang接受这一点。我需要重新检查一下规格,看看是否正确

更新:规范上说

自动类型说明符表示所声明变量的类型应从其初始值设定项中推导出来,或表示函数声明符应包含一个尾随返回类型

这在快速读取时可能会产生误导,但这是由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);
模板
结构参数;
模板
结构参数{
模板
使用恒等式=T;
模板
静态标识*get(rf(参数…){
返回f;
}
};
//现在很容易了
自动条=参数::获取(foo);

rational只是因为它们是不同的类型。你可以用
重新解释@StevenBurnap来覆盖这一点(风险自负):问题是为什么他们不能转换。很明显,它们是不同的类型。
float
也不是
int
,但您可以转换它们。我猜这是因为一般规则不允许隐式转换指针。请记住
std::function baz已经必须对这两个
baz=[]{}执行魔法
(这里是一个函子)和
baz=qux(此处假设一个函数)工作。与
void(*)(
)的比较并没有那么神奇,这是一种延伸。不要认为对一个的限制应该适用于后者。(并不是说这个问题一点都不有趣。)甚至这个问题也在VS2011 RC中编译。自动条=测试;bar();
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);