【问题标题】:How to assert a function as final for CRTP?如何将函数声明为 CRTP 的最终函数?
【发布时间】:2020-01-10 16:13:01
【问题描述】:

正如我们在 CRTP 中所知道的,派生类继承基类,作为最终继承。

如果我们想让派生类不是最终的,但“覆盖”函数是“最终的”怎么办?

有什么办法可以用 static_assert 实现吗?

代码示例:

template <typename D>
struct A
{
    int f()
    {
        return static_cast<D*>(this)->g();
    }

    int g();
};

struct B : A<B> // usually final, but we want it inheritable
{
    int g() // but this should be 'final'
    {
        // TODO: ???
        return 1;
    }
};

struct C : B
{
    int g() // this is bad
    {
        return 2;
    }

    int h(); // this is permissive
};

#include <iostream>

template <typename D>
void f(A<D>& x)
{
    std::cout << x.f() << std::endl;
}

int main()
{
    B b;
    C c;
    f(b); // OK, it's 1
    f(c); // BAD, it's 1
    return 0;
}

【问题讨论】:

  • “如果我们想让派生类不是最终的,但'覆盖'函数是'最终的'怎么办?” - 我看到 no 在您的代码中的任何地方使用final(或override)。既不是类也不是函数..
  • @JesperJuhl 我们想要动态多态中的final 之类的东西,它将final 应用于函数而不是类,CRTP 中没有virtualoverridefinal跨度>

标签: c++ crtp


【解决方案1】:

您可以将final 用于两个目的。

来自https://en.cppreference.com/w/cpp/language/final

指定一个虚函数不能在派生类中被覆盖或者一个类不能被继承。

你可以使用

struct B : A<B>
{
    virtual int g() final
    {
        return 1;
    }
};

允许其他类从B 派生但不能覆盖g()

使用final 的另一个潜在好处是优化编译器可能能够在编译时解析函数调用,而不是在运行时解析(感谢@JesperJuhl)。

【讨论】:

  • 除了不继承/不覆盖语义之外,可能还可以提及 - 在 某些 情况下 - 使用 @987654327 @ 还可以帮助优化编译器更好地去虚拟化函数调用。
  • @JesperJuhl,很有趣。我没有从这个角度考虑过。谢谢。
  • 要在那里使用final,成员函数需要是virtual。但是这种情况使得 CRTP 在这种情况下有点没用,因为它试图避免使用虚函数表,而是依赖于编译时静态多态性。嗯。 CRTP 将转发到 B::g 而不是非虚拟覆盖的 C::g (如果这对 OP 的用例有用的话)。
  • 您的更改不会编译,因为 int g() 不是 virtualfinal 仅适用于类或虚函数。
  • 这个解决方案很简单,但是引入了一个vptr,甚至可以优化动态分辨率,增加对象大小。
【解决方案2】:

我想出了一个解决方案,在函数签名中使用私有标签:

template <typename D>
struct A
{
    struct internal_tag
    {};

    int f()
    {
        return static_cast<D*>(this)->g({});
    }

    int g(internal_tag);
};

struct B : A<B>
{
    int g(internal_tag)
    {
        return 1;
    }

private:
    using A<B>::internal_tag;
};

struct C : B
{
    //  int g(internal_tag) // int g(internal_tag) is prohibited
    //  {
    //      return 2;
    //  }

    int h();
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 1970-01-01
    相关资源
    最近更新 更多