这里我有一个简单的 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)。 Impl
在base<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/