[英]Can modern compilers devirtualize function calls when using dependency injection
当您希望代码可测试时,依赖注入是一种非常重要的模式,并且它一直在大型 C++ 项目中使用。
这是一个简单的例子:
// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0
class FooInterface {
public:
virtual void f() = 0;
virtual ~FooInterface() = default;
};
class Foo : public FooInterface {
public:
void f() override { /* Actual work */ }
};
class MockFoo : public FooInterface {
public:
void f() override { /* Mock code */ }
};
class Bar {
public:
Bar(std::unique_ptr<FooInterface>&& foo) : foo_(std::move(foo)) { }
void b() { foo_->f(); }
private:
std::unique_ptr<FooInterface> foo_;
};
// In production
void production() {
auto bar = std::make_unique<Bar>(std::make_unique<Foo>());
bar->b();
// ...
}
// In tests
void test() {
auto bar = std::make_unique<Bar>(std::make_unique<MockFoo>());
bar->b();
// ...
}
我一直有一个疑问是编译器是否能够将这种虚拟函数的使用进行去虚拟化。
我知道在一般情况下不可能知道正在使用哪个派生类,但是在上面的示例中,编译器知道在production
和test
函数中使用了哪些对象,因为它们被显式实例化。
现代编译器是否可以优化这种特殊情况并删除虚函数调用,因为它们知道在编译时正在实例化哪个派生类?
如果此特定示例无法去虚拟化,是否可以对其进行任何更改以使调用去虚拟化?
这取决于编译器和您正在处理的特定代码。 根据我的经验,如果您真的关心避免virtual
调用,则不应依赖它。
例如,如果f()
是noexcept
,Clang/LLVM将在-O2
下将您的示例去noexcept
:
class FooInterface {
public:
virtual void f() noexcept = 0;
virtual ~FooInterface() = default;
};
但其他主要编译器不会。 你的旅费可能会改变。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.