函数的返回类型和它作为参数的类型应该相同

Should return type of a function and its type as argument be the same?

本文关键字:类型 参数 返回类型 函数      更新时间:2023-10-16

在这里我找到了这段代码(我已经修改了一点):

template<typename F>     
void Eval( const F& f ) {
     f(3.14);
}

据我所知,Eval函数接受另一个函数作为参数,并返回其参数的特定值(在上述情况下为3.14)。但是我不确定应该用什么作为函数的类型?它的返回类型?那么,应该这样称呼它吗?:

Eval<float>(sin)
此外,为什么要使用指针(&-symbol)呢?我们通过引用传递参数是因为我们想要改变它吗?我们要在Eval函数中重新定义函数吗?如果是,为什么我们不这么做?如果不是,为什么我们需要&象征?

Eval是一个函数模板。从这个模板实例化的函数有返回类型void,这意味着它不返回值。它只是在内部调用f(3.14),然后返回给调用者。

其形参类型为,常量引用类型为F,其中F为模板形参。通过const-reference传递意味着不能在函数内部修改对象,但是不进行复制。在函数内部,f将只是传入的对象(或函数)的另一个名称。

Eval显然希望它的参数是一个可调用类型:一个函数、一个指向函数的指针或一个定义了operator()的类的对象。因此,不能将模板参数指定为double。如果有的话,应该是double(double) = function接受双精度体并返回双精度体。但是,大多数情况下,您根本不需要指定模板实参:它将从您传入的实际实参中推导出来。这样的:

void myFun(double x)
{
  std::cout << x << 'n';
}
void someFun()
{
  Eval(myFun);
  Eval([](double a){ global_var = a; });
}

但是我不确定应该用什么作为函数的类型?

函数类型看起来像float(float)

或者你指的是返回类型?在现代c++中,您可以推断返回类型:

template <typename F>
auto Eval(const F & f) -> decltype(f(3.14)) {
    return f(3.14);
}

如果您坚持使用该语言的旧版本,那么它就相当棘手了。您可以为普通函数和包含result_type定义的函数类型(例如从std::unary_function派生的函数类型)提供重载:

template <typename Ret, typename Arg>
Ret eval(Ret (&f)(Arg)) {return f(3.14);}
template <typename F>
typename F::result_type eval(F const & f) {return f(3.14);}

可以通过定义一个traits类,或者使用Boost的函数traits

来泛化这个

那么,应该这样调用它吗?

如果只有一个函数具有该名称,则可以使用依赖参数的查找:

Eval(sin);

然而,如果这是std::sin,那么就会有多个重载,所以你必须指定类型:

Eval<float(float)>(sin);
此外,为什么要使用指针(&-symbol)呢?

我们没有。那是引用,不是指针。

通过引用传递实参是因为我们想改变它吗?

。这是对const的引用,所以我们不能改变它。

如果没有,为什么我们需要&象征?

有些类型的复制成本很高;有些是完全不能复制的;但是所有类型都可以通过引用传递。因此,一个完全泛型的函数模板需要通过引用来获取它的参数,以便对所有类型都可用。

你传递的是一个可调用类型,这意味着它要么是一个std::function<double(double)>,要么是一个指向函数(double)(fooptr*)(double)的函数指针,要么是一个类/结构体,它被doubule operator()(double)重载,所以它可以像函数一样被调用。

Eval中有一个f()的事实给了你任何传递给F的类型应该可以被()调用的线索

下面是一个你可以使用它的例子:

#include<iostream>
template<typename F>     
void Eval( const F& f ) {
    std::cout << f(3.14);
}
double foo(double d)
{
   return d + d;
}
int main()
{
 Eval(foo);
}

实例

在c++中模板是在编译过程中实例化的,所以如果你传递任何支持调用的东西,它都会工作。

例如:

class MyClass
{
public:
    void operator() (double d)
    {
        // Do something
    } 
}
MyClass m;
Eval(m);

或:

Eval([](double d) { /* ... */ });

或:

void DoSth(double d)
{
    // ...
}
Eval(DoSth);

为什么要传递const引用?原因如下:

  • 传递的对象没有被复制,这提高了性能;
  • 不能传递不存在的对象(引用不能为null)
  • const在const引用中保证对象本身不会在函数调用中被改变

如果您想要返回值,您将希望使用如下内容

template<typename F>     
typename F::result_type Eval(F f) {
     return f(3.14);
}
指出

    按值传递函数更习惯。
  1. 这可能不适用于void返回类型。
  2. 功能必须具有适应性
  3. 你不需要传递模板参数,因为编译器可以自己计算。

据我所知,Eval函数将另一个函数作为参数并返回其值

No, Eval返回void。没有使用任何f(3.14) return。

但是我不确定应该用什么作为函数的类型?它的返回类型?那么,应该这样称呼它吗?:Eval<float>(sin)

在大多数情况下,Eval(obj)就足够了,F类型将从obj类型"推断"出来。如果这是模棱两可的,你需要明确:Eval<float(float)>(sin)(我希望你包括正确的头文件)。任何可以用double参数"调用"的类型的"对象"都将被编译。

Moreover, why do we use pointers (&-symbol)? 

。这里的const&表示const引用。您直接访问参数,但承诺不更改它。