Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/144.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++ 用于编写平台无关代码的继承vs std::函数_C++_Optimization_Compiler Optimization - Fatal编程技术网

C++ 用于编写平台无关代码的继承vs std::函数

C++ 用于编写平台无关代码的继承vs std::函数,c++,optimization,compiler-optimization,C++,Optimization,Compiler Optimization,我正在评估使用lambdas而不是继承来编写平台无关代码的可能性。我经常需要传递回调(用于日志记录、硬件通信等)。 通常,人们会使用继承和虚拟函数来实现想要做的事情。在下面的示例中,test类将在不同平台和编译器(从PC到小型MCU)之间的单独文件中共享。 priv_test将由test类的用户定义,并且取决于项目 volatile int a = 0; struct test { virtual int callback(int i) = 0; test() {}

我正在评估使用lambdas而不是继承来编写平台无关代码的可能性。我经常需要传递回调(用于日志记录、硬件通信等)。 通常,人们会使用继承和虚拟函数来实现想要做的事情。在下面的示例中,
test
类将在不同平台和编译器(从PC到小型MCU)之间的单独文件中共享。
priv_test
将由
test
类的用户定义,并且取决于项目

volatile int a = 0;

struct test
{
    virtual int callback(int i) = 0;

    test()
    {}

    void do_something()
    {
        a += callback(5);
    }
};

struct priv_test:public test
{
    using test::test;
    int callback(int i) override
    {
        return i+2;
    }
};

int main()
{
    priv_test instance;
    instance.do_something();
}
在这个简单的示例中,编译器输出得到了很好的优化:

另一种方法是像这样使用lambda:

#include <functional>

volatile int a = 0;

struct test
{
    struct conf
    {
        std::function<int(int)> callback;
    };

    test(const conf& config) : config(config)
    {}

    void do_something()
    {
        a += config.callback(5);
    }

private:
    const conf& config;
};

int main()
{
    test::conf config{[](int i) {return i+2;}};

    test instance(config);
    instance.do_something();
}
#包括
挥发性INTA=0;
结构测试
{
结构形态
{
函数回调;
};
测试(const conf&config):配置(config)
{}
空做某事
{
a+=config.callback(5);
}
私人:
const-conf&config;
};
int main()
{
test::conf config{[](int i){return i+2;}};
测试实例(config);
例如:做某事;
}
在后一种情况下,它对于最终用户来说非常容易使用,并且非常灵活。但是,编译器输出相当大,即使它实现了相同的功能(在本例中):

  • 那么,您将如何编写需要某种回调的类,为什么
  • 您是否认为其他代码体系结构比上面提供的更好
  • 第二个版本可以进一步优化吗

  • PS:代码大小和性能对我很重要。通常我只有一些此类类的实例。

    听起来像是可以轻松进行基准测试的东西。这看起来不像是一个公平的比较。第一个版本要小得多,因为
    实例的类型在编译时已知,所以所有内容都是静态调度、内联和简化的。第二个版本是真正的动态调度。它还包含一个堆分配(您的第一个版本没有),并检查一个空的
    std::function
    。除非您的实际情况不需要堆分配,不可能缺少/无效函数,并且具有静态已知调用(在这种情况下,您应该使用函子),否则此示例不具有代表性。@HTNW谢谢您的回答。我不熟悉函子。为什么在编译时一切都已知的情况下,它们更适合呢?我的怀疑得到了证实:,它的大小与
    std::function
    版本相当。实际上,在我的机器上,使用
    -Os
    std::function
    版本的目标文件要小几个字节,可执行文件的大小是虚拟函数版本的2/3。。。我不知道为什么。(我也尝试过使用普通的
    *
    ,但编译器看穿了这一点,一切都是静态的,所以再次不是一个好的比较。)finickyness只是告诉你衡量这些东西有多么困难。无论如何:使用functor意味着使用模板,这意味着在编译时必须知道一切,也意味着编译器可以进行更多的优化(希望如此)。函数/类将函子对象作为参数/字段(
    template foo(F callback);
    ),然后调用
    操作符()
    s(
    {int i=…;callback(i);}
    )。编译器为每个不同的
    F
    生成
    foo
    的副本,因此它可以将
    F::operator=
    内联到
    foo
    。Lambda表达式是一个新的匿名“函子类”的缩写,它们创建函子对象,支持这种模式。听起来像是可以轻松进行基准测试的东西。这看起来不像是一个公平的比较。第一个版本要小得多,因为
    实例的类型在编译时已知,所以所有内容都是静态调度、内联和简化的。第二个版本是真正的动态调度。它还包含一个堆分配(您的第一个版本没有),并检查一个空的
    std::function
    。除非您的实际情况不需要堆分配,不可能缺少/无效函数,并且具有静态已知调用(在这种情况下,您应该使用函子),否则此示例不具有代表性。@HTNW谢谢您的回答。我不熟悉函子。为什么在编译时一切都已知的情况下,它们更适合呢?我的怀疑得到了证实:,它的大小与
    std::function
    版本相当。实际上,在我的机器上,使用
    -Os
    std::function
    版本的目标文件要小几个字节,可执行文件的大小是虚拟函数版本的2/3。。。我不知道为什么。(我也尝试过使用普通的
    *
    ,但编译器看穿了这一点,一切都是静态的,所以再次不是一个好的比较。)finickyness只是告诉你衡量这些东西有多么困难。无论如何:使用functor意味着使用模板,这意味着在编译时必须知道一切,也意味着编译器可以进行更多的优化(希望如此)。函数/类将函子对象作为参数/字段(
    template foo(F callback);
    ),然后调用
    操作符()
    s(
    {int i=…;callback(i);}
    )。编译器为每个不同的
    F
    生成
    foo
    的副本,因此它可以将
    F::operator=
    内联到
    foo
    。Lambda表达式是新的匿名“函子类”的缩写,它们创建函子对象,支持这种模式。