這里我有一個簡單的 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都用于基類。
一般來說,基類是否可以使用 typedef 或 constexprs Impl?
uj5u.com熱心網友回復:
根本區別在于您嘗試訪問Impl類內部的那一刻。Implinbase<Impl>是一個不完整的型別,你可以用它做什么有一些限制。
特別是,您無法訪問num內部的資料成員base,這就是該行的原因
constexpr static std::make_index_sequence<Impl::num> idx{};
導致編譯錯誤。請注意,要定義base類,編譯器必須知道此時Impl::numright的值。
與此相反,在您的第一個示例中,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{};
};
一般來說,基類是否可以使用 typedef 或 constexprs
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不必是完整的型別。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/397259.html
