c++ - 简单 CRTP 案例中没有名为 "XXX"的成员

标签 c++ templates crtp

这里我有一个简单的 CRTP 案例:

#include <cstddef>
#include <utility>

template <typename Impl>
class base
{
    constexpr static size_t impl_num = Impl::num;
};

template <typename Impl>
class deriv : public base<deriv<Impl>>
{
    friend class base<deriv<Impl>>;

    constexpr static size_t num = Impl::num_in;
};

class actual_impl
{
public:
    constexpr static size_t num_in = 10;
};

using my_type = deriv<actual_impl>;

int main()
{
    my_type a{};
}

这段代码编译得很好,但是当我将基类更改为:

#include <cstddef>
#include <utility>

template <typename Impl>
class base
{
    constexpr static std::make_index_sequence<Impl::num> idx{};
};

template <typename Impl>
class deriv : public base<deriv<Impl>>
{
    friend class base<deriv<Impl>>;

    constexpr static size_t num = Impl::num_in;
};

class actual_impl
{
public:
    constexpr static size_t num_in = 10;
};

using my_type = deriv<actual_impl>;

int main()
{
    my_type a{};
}

Clang 提示 error: no member named 'num' in 'deriv<actual_impl>' 。我只是很困惑为什么第一种情况有效但第二种情况不起作用,这两者之间的根本区别是什么,因为在我看来,在这两种情况下 Impl::num_in在基类中使用。

一般来说,基类是否可以使用 Impl 中的 typedef 或 constexpr ?

最佳答案

根本的区别在于当您尝试访问Impl的内部时。类(class)。 Implbase<Impl>是一个不完整的类型,并且您可以用它做什么有一定的限制。

特别是,您无法访问num base里面的数据成员,这就是为什么该行

constexpr static std::make_index_sequence<Impl::num> idx{};

导致编译错误。请注意,要定义 base类,编译器必须知道 Impl::num 的值就在那一刻。

与此相反,在第一个示例中,Impl::num仅用于初始化 impl_num 的值,否则不依赖于 Impl::num 。该初始化的实例化稍后发生,此时 Impl成为一个完整的类型。因此,没有错误。

如果稍微改变一下定义,

template<typename Impl>
class base {
    constexpr static decltype(Impl::num) impl_num = Impl::num;
    // or 
    constexpr static auto impl_num = Impl::num;
}

并制作impl_num 类型取决于 Impl ,你会因为同样的原因得到同样的错误。

添加间接没有帮助,以下代码也无法编译:

template<typename Impl>
class base {
    constexpr static size_t impl_num = Impl::num;
    constexpr static std::make_index_sequence<impl_num> idx{};
};

In general, is it possible for base class to use typedefs or constexprs from Impl?

这要看情况。您只能在 Impl 发生实例化的上下文中使用它们。是一个完整的类型。例如,

template<typename Impl>
class base {
public:
    void foo() {
        decltype(Impl::num) impl_num = 0;
    }
};

很好,但是

template<typename Impl>
class base {
public:
    decltype(Impl::num) foo() { 
        return 0;
    }
};

不是。

避免 CRTP 中不完整类型潜在问题的标准技巧是引入辅助特征类:

// Just forward declarations
template<typename Impl> class deriv;
class actual_impl;

using my_type = deriv<actual_impl>;

template<class> struct traits;
template<> struct traits<my_type> {
    using num_type = std::size_t;
};

template <typename Impl>
class base {
public:
    typename traits<Impl>::num_type foo() {
        return 0;
    }
};

// Now actual definitions
// ...

在这里,访问traits<Impl>内部结构,Impl不必是完整的类型。

关于c++ - 简单 CRTP 案例中没有名为 "XXX"的成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70503372/

相关文章:

C++:将指向一个对象的成员函数的指针存储在另一个对象中

css - html 模板中的样式标签(在 Aurelia View 中)?

c++ - CRTP。试图理解给定的例子

c++ - 如何在编译或运行时检测非虚拟覆盖

c++ - 缩短作为模板参数传递到 CRTP ("pass me"中的长模板派生类)

C++ assert() 失败,没有给出任何错误消息,也没有给出失败的行

java - Java 中的尾递归编程

c++ - 子类化 STL 容器 : Range Constructors don't work

c++ - 为什么此代码工作/失败取决于范围关闭创建于?

c++ - QGraphicsView::drawBackground 中的故障网格