草庐IT

c++ - 本地实现的接口(interface)引用

coder 2024-02-24 原文

请考虑以下代码:

struct A
{
    virtual ~A() {}
    virtual int go() = 0;
};

struct B : public A { int go() { return 1; } };

struct C : public B { int go() { return 2; } };

int main()
{
    B b;
    B &b_ref = b;

    return b_ref.go();
}

在 GCC 4.4.1 下(使用 -O2 ),调用 B::go()得到内联(即,没有虚拟分派(dispatch)发生)。这意味着编译器承认 a_ref确实指向一个 B类型变量。 B引用可用于指向 C ,但编译器足够聪明,可以预见情况并非如此,因此它完全优化了函数调用,内联函数。

太棒了!这是一个令人难以置信的优化。

但是,那么,为什么 GCC 在以下情况下不做同样的事情呢?

struct A
{
    virtual ~A() {}
    virtual int go() = 0;
};

struct B : public A { int go() { return 1; } };

struct C : public B { int go() { return 2; } };

int main()
{
    B b;
    A &b_ref = b;

    return b_ref.go(); // B::go() is not inlined here, and a virtual dispatch is issued
}

有什么想法吗?其他编译器呢?这种优化常见吗? (我对这种编译器洞察力很陌生,所以我很好奇)

如果第二种情况可行,我可以创建一些非常棒的模板,例如:

template <typename T>
class static_ptr_container
{
public:
    typedef T st_ptr_value_type;

    operator T *() { return &value; }
    operator const T *() const { return &value; }

    T *operator ->() { return &value; }
    const T *operator ->() const { return &value; }

    T *get() { return &value; }
    const T *get() const { return &value; }

private:
    T value;
};

template <typename T>
class static_ptr
{
public:
    typedef static_ptr_container<T> container_type;
    typedef T st_ptr_value_type;

    static_ptr() : container(NULL) {}
    static_ptr(container_type *c) : container(c) {}

    inline operator st_ptr_value_type *() { return container->get(); }
    inline st_ptr_value_type *operator ->() { return container->get(); }

private:
    container_type *container;
};

template <typename T>
class static_ptr<static_ptr_container<T>>
{
public:
    typedef static_ptr_container<T> container_type;
    typedef typename container_type::st_ptr_value_type st_ptr_value_type;

    static_ptr() : container(NULL) {}
    static_ptr(container_type *c) : container(c) {}

    inline operator st_ptr_value_type *()  { return container->get(); }
    inline st_ptr_value_type *operator ->()  { return container->get(); }

private:
    container_type *container;
};

template <typename T>
class static_ptr<const T>
{
public:
    typedef const static_ptr_container<T> container_type;
    typedef const T st_ptr_value_type;

    static_ptr() : container(NULL) {}
    static_ptr(container_type *c) : container(c) {}

    inline operator st_ptr_value_type *() { return container->get(); }
    inline st_ptr_value_type *operator ->() { return container->get(); }

private:
    container_type *container;
};

template <typename T>
class static_ptr<const static_ptr_container<T>>
{
public:
    typedef const static_ptr_container<T> container_type;
    typedef typename container_type::st_ptr_value_type st_ptr_value_type;

    static_ptr() : container(NULL) {}
    static_ptr(container_type *c) : container(c) {}

    inline operator st_ptr_value_type *() { return container->get(); }
    inline st_ptr_value_type *operator ->() { return container->get(); }

private:
    container_type *container;
};

在许多情况下,这些模板可用于避免虚拟分派(dispatch):

// without static_ptr<>
void func(B &ref);

int main()
{
    B b;
    func(b); // since func() can't be inlined, there is no telling I'm not
             // gonna pass it a reference to a derivation of `B`

    return 0;
}

// with static_ptr<>
void func(static_ptr<B> ref);

int main()
{
    static_ptr_container<B> b;
    func(b); // here, func() could inline operator->() from static_ptr<> and
             // static_ptr_container<> and be dead-sure it's dealing with an object
             // `B`; in cases func() is really *only* meant for `B`, static_ptr<>
             // serves both as a compile-time restriction for that type (great!)
             // AND as a big runtime optimization if func() uses `B`'s
             // virtual methods a lot -- and even gets to explore inlining
             // when possible

    return 0;
}

实现它是否可行? (并且不要继续说这是一个微优化,因为它很可能是一个巨大的优化..)

-- 编辑

我刚刚注意到 static_ptr<> 的问题与我暴露的问题无关。指针类型被保留,但它仍然没有内联。我想 GCC 只是没有深入到发现 static_ptr_container<>::value 既不是引用也不是指针的程度。对于那个很抱歉。但问题仍然没有答案。

-- 编辑

我已经制定了一个版本 static_ptr<>这确实有效。我也稍微更改了名称:

template <typename T>
struct static_type_container
{
    // uncomment this constructor if you can't use C++0x
    template <typename ... CtorArgs>
    static_type_container(CtorArgs ... args)
            : value(std::forward<CtorArgs>(args)...) {}

    T value; // yes, it's that stupid.
};

struct A
{
    virtual ~A() {}
    virtual int go() = 0;
};

struct B : public A { int go() { return 1; } };

inline int func(static_type_container<Derived> *ptr)
{
    return ptr->value.go(); // B::go() gets inlined here, since
                            // static_type_container<Derived>::value
                            // is known to be always of type Derived
}

int main()
{
    static_type_container<Derived> d;
    return func(&d); // func() also gets inlined, resulting in main()
                     // that simply returns 1, as if it was a constant
}

唯一的缺点是用户必须访问 ptr->value得到实际的对象。重载operator ->()在海湾合作委员会中不起作用。任何返回对实际对象的引用的方法(如果它是内联的)都会破坏优化。真可惜..

最佳答案

这不是一个明确的答案,但我想我还是可以发布它,因为它可能对某些人有用。

评论者Julio Guerra指出了一个名为静态 C++ 面向对象编程 (SCOOP) 的 C++ 习语(他们在论文中称其为“范式”,但我认为这有点过分)。我将发布此消息以提高 SCOOP 的知名度。

SCOOP 的发明是为了让 C++ 程序员能够在 C++ 中很好地协同工作,从而充分利用 OOP 和 GP 世界。它主要针对科学编程,因为它可以带来性能提升,并且可以用于提高代码表达能力。

SCOOP 使 C++ 泛型类型似乎模拟了传统面向对象编程的所有方面 — 静态地。这意味着模板方法具有一些特性,例如,能够正确重载和(显然)发出比通常由您的临时模板函数引起的错误消息更正确的错误消息。

它也可以用来做一些有趣的技巧,比如条件继承。

我试图用 static_ptr<> 完成什么恰恰是一种静态的面向对象。 SCOOP 将其提升到一个全新的水平。

对于那些感兴趣的人,我发现有两篇论文讨论了这个问题:A Static C++ Object-Oriented Programming (SCOOP) Paradigm Mixing Benefits of Traditional OOP and Generic ProgrammingSemantics-Driven Genericity: A Sequel to the Static C++ Object-Oriented Programming Paradigm (SCOOP 2) .

不过,这个习语并非没有缺点:它是那些不常见的事情之一,应该是你最后的选择,因为人们很可能很难弄清楚你做了什么,等等。你的代码也会变得更加冗长和您可能会发现自己无法做您认为可能的事情。

不过,我确信它在某些情况下仍然有用,更不用说真正的乐趣了。

快乐的模板破解。

关于c++ - 本地实现的接口(interface)引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4372569/

有关c++ - 本地实现的接口(interface)引用的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  5. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  10. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

随机推荐