对比解决方案,用于从基类模板继承时的可见性

Contrasting solutions for visibility when inheriting from base class templates

提问人:Adrian McCarthy 提问时间:8/21/2023 最后编辑:Adrian McCarthy 更新时间:8/21/2023 访问量:37

问:

在下面的代码中,编译将失败,因为尽管有公共继承链,但 HasFlag 在 DerivedFoo 中不可见。

class BasicFoo {
    public:  bool HasFlag() const { return m_flag; }
    private: bool m_flag = false;
};

template <typename T>
class Foo : public BasicFoo {
    public:  bool Test() const { return HasFlag(); }
};

template <typename T>
class DerivedFoo : public Foo<T> {
    public:
        // HasFlag is not visible here.
        bool Test2() const { return HasFlag(); }
};

为什么会这样,在其他问题中也有介绍。我的问题是这两种解决方案之间是否有任何区别:

        // Option 1:  Explicitly pull it into scope from immediate base.
        using Foo<T>::HasFlag;
        bool Test2() const { return HasFlag(); }
        // Option 2:  Explicitly pull it from the root class.
        using BasicFoo::HasFlag;
        bool Test2() const { return HasFlag(); }

从不是类模板的根类中提取方法是否在某种程度上更有效?只有一个 BasicFoo::HasFlag。如果为许多 T 实例化了 DerivedFoo<T>,则实际上存在许多相同的 Foo<T>::HasFlags。这重要吗?

C++ 继承 using 指令

评论

0赞 273K 8/21/2023
如果为许多 T 实例化了 DerivedFoo<T>,则实际上存在许多相同的 Foo<T>::HasFlags这是错误的。 不是由 定义的,因此实例化不会实例化很多。HasFlagsFoo<T>Foo<T>HasFlags
1赞 Igor Tandetnik 8/21/2023
在 中,使它 .演示。这并不是说不可见 - 而是在看到实现时,编译器无法确定实际上包含成员。就其所知,有一个专门化不是从 .因此,诀窍是创建一个依赖名称,将其查找推迟到实例化点。DerviedFooreturn this->HasFlags();HasFlagsDerviedFoo::Test2Foo<T>HasFlagsFooBasicFooHasFlags
0赞 Adrian McCarthy 8/21/2023
@IgorTandetnik:谢谢。我知道使用 this 指针可以解决方法实现中的问题,但它不会将名称引入公共接口,从而使类的外部用户无法访问旨在供公共使用的方法。
0赞 Igor Tandetnik 8/21/2023
我不确定我是否理解。外部客户端可以很好地执行类似 .在什么意义上不是公共接口的一部分?你想做什么,是什么阻止你去做?您能否显示使用而不是声明时无法编译的代码?DerivedFoo<int> d; d.HasFlag();HasFlagthis->HasFlags()using
0赞 Adrian McCarthy 8/21/2023
@237K:我没说得好。我知道 HasFlag 方法只有一个实例。不过,我可以想象,在编译代码时,编译器可能必须为每个 Foo< 的表添加一个条目T>引用回该方法的一个实例。

答:

1赞 Jan Schultke 8/21/2023 #1

从语义上讲,这两种解决方案是等价的。它们都是 using 声明,使成员在派生类中可用。HasFlag

using BasicFoo::HasFlag;

不过,这可能会更好,因为它不依赖于 ,并且编译器可以在实例化类模板之前诊断该成员是否存在。从直觉上讲,这应该会稍微快一些,尽管它不太可能产生任何明显的差异。如有疑问,请对您的编译进行基准测试(例如,使用 LLVM 的)。THasFlagBasicFooDerivedFoo-ftime-trace

如果实例化了许多 s,则实际上有许多相同的 .DerivedFoo<T>TFoo<T>::HasFlags

事实并非如此;中只有一个成员。这两种解决方案之间的区别仅在于名称查找,而不在于创建的成员数量。HasFlagBasicFoo

替代解决方案

bool Test2() const {
    // access to this makes HasFlag() dependent on T, so this won't fail to compile
    return this->HasFlag();
}

bool Test2() const {
    // qualified lookup also forces a delayed check, making this compile
    return BasicFoo::HasFlag();
}

评论

0赞 Adrian McCarthy 8/21/2023
谢谢,关于可能使编译器推迟 DerivedFoo 实例化的一点达到了我所追求的核心。我知道您包含的替代解决方案,但我也在尝试向 DerivedFoos 的用户公开 HasFlag。