【问题标题】:What is the effect of `const` modifier and reference in the template argument of std::functionstd::function 的模板参数中的 `const` 修饰符和引用有什么作用
【发布时间】:2012-03-13 05:54:58
【问题描述】:

我不确定 const 修饰符和引用在 std::function 的模板参数中的效果。例如,在下面的代码中,我应该使用std::function<bool(std::string, std::string)> 还是std::function<bool(const std::string&, const std::string&)> 作为基类?我在 GCC 4.4 中测试了两者,但是没有区别。提前致谢。

#include <iostream>
#include <functional>
#include <string>

//struct FLess : public std::function<bool(std::string, std::string)>
struct FLess : public std::function<bool(const std::string&, const std::string&)>
{
    bool operator () (const std::string& s1, const std::string& s2) const
    {
        return s1 < s2;
    }
};

int main(int argc, char* argv[])
{
    FLess f;
    std::string a = "a";
    std::string b = "b";
    std::cerr << f(a, b) << std::endl;
    return 0;
}

【问题讨论】:

  • 从技术上讲,您都不应该使用。您根本不应该从 std::function 派生。你没有从超载中获得任何好处。它看起来更像是你想把你的FLessinside 放在std::function 中,而不是从它派生而来。
  • 致@NicolBolas,我记得在C++03 中,如果函数不是从std::unary_functionstd::binary_function 派生的,它在某些需要诸如first_argument_type 之类的类型的情况下可能会失败, result_type 待定义。所以我在 C++11 中以类似的方式使用它(源自std::function)。
  • @YunHuang:std::function 与那些旧的(现在已弃用的)类非常不同,从它继承很可能会引起混淆。很容易分割你的类并丢失运算符重载 - 但没有编译器错误,只是奇怪的运行时行为。如果你真的需要使用像 result_type 这样需要 typedef 的遗留代码,你最好自己定义它们,或者可能继承自 std::binary_function
  • +1 好问题,虽然你的例子中的继承混淆了这个问题。

标签: c++ function c++11 constants


【解决方案1】:

您不应该从std::function 继承。

您可以像这样抽象底层的函数对象:

void DoSomething(function<void(const string&, const string&) myF)
{
    string s1, s2;
    myF(s1, s2);
}

// usage:

DoSomething(bind(otheFunc, ....));
DoSomething([](const string& s1, const string& s2) { ... });

struct Func
{
    operator()(const string& s1, const string& s2)
    { ... }
}

Func f;
DoSomething(f);

现在回答您的问题,如果您使用const string&amp;,您是在要求编译器不要复制对象并禁止修改。该选择取决于您赋予参数的含义。

对于像数字和小型结构这样的小类型,通过副本传递。

除非您想要执行非常高级的复制/移动优化,否则您最好始终将const&amp; 用于大型类型。我认为string 是一个大类型,除非你确定它永远不会增长。

【讨论】:

    【解决方案2】:

    归结为函数对象的参数类型与它所引用的可调用实体的参数类型之间的兼容性,如果函数的参数类型可以转换为可调用实体参数类型:

    void foo(double x, double y);
    void bar(const double& x, const double& y);
    void fooBar(double& x, double& y);
    
    std::function<void(const double&, const double&)> f;
    
    f = &foo; // OK
    f = &bar; // OK
    f = &fooBar; // Error on GCC 4.7. Cannot instantiate a double& from const double.
    

    有趣的是,具有 void 返回类型的 std::function 与具有任何返回类型的可调用实体兼容:

    int fooBarFoo(const double& x, const double& y);
    
    f = &fooBarFoo; // OK
    

    因此,在您比较通过 const 引用传递而不是通过值传递的情况下,我认为没有明显的区别。

    【讨论】:

    • 是的,这正是我想要的。谢谢。
    【解决方案3】:

    传递对 const 对象的引用避免了复制对象只是为了传递给函数。在您的情况下(一个小字符串),它可能没有引起足够的注意。

    如果您正在处理(比如说)几兆字节的字符串,通过引用传递给 const 将避免为(和复制)所有数据分配空间。

    同时,大多数编译器在内部实现引用的方式非常类似于指针。由于额外的间接级别,这可能会导致执行速度变慢。对于小项目(char、int、可能是 long),您通常更喜欢按值传递。对于较大的项目(可能是长字符串、矩阵等),您通常更喜欢通过引用传递。如果有疑问,通常通过引用传递——在这个方向上犯错的惩罚通常是相当小的。

    【讨论】:

    • 感谢您的回复。正如我对operator () 进行了按引用传递一样,我知道按值传递和按引用传递之间的区别。我的问题是:如果我只更改基类定义,似乎没有区别。因为FLess 只有一个operator ()(没有重载),所以编译器似乎忽略了std::function 的参数。我不确定。
    猜你喜欢
    • 2014-01-03
    • 2021-10-30
    • 1970-01-01
    • 2022-01-05
    • 2017-10-10
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    相关资源
    最近更新 更多