我正在嘗試將 CRTP 型別引數傳遞給虛擬方法。因此,虛方法需要是一個模板。但是,這在 C 中是不允許的(還沒有?),因為這意味著 vtable 的大小——編譯器實作動態分派的常見方式——在所有源都被編譯并鏈接之前是未知的。(我在搜索 SO 時發現了這個推理。)
但是,在我的特定環境中,CRTP 專業化的數量是有限且已知的。因此,可以為每個專業定義一個虛擬方法多載并在子類中覆寫它們。我準備了一個小型 MWE 來演示我的情況。考慮以下 CRTP 層次結構:
template<typename Actual>
struct CRTPBase
{
using actual_type = Actual;
void foo() { static_cast<actual_type*>(this)->foo(); }
int bar(int i) const { return static_cast<const actual_type*>(this)->bar(i); }
};
struct A : CRTPBase<A>
{
void foo() { /* do something A would do */ }
int bar(int i) const { return i 1; }
};
struct B : CRTPBase<B>
{
void foo() { /* do something B would do */ }
int bar(int i) const { return i - 1; }
};
接下來,我想定義一個具有虛擬方法的虛擬類層次結構來處理CRTPBase<T>. 因為我知道特定的專業,我可以這樣做:
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(const CRTPBase<A> &o) = 0;
virtual void accept_crtp(const CRTPBase<B> &o) = 0;
};
struct VirtualDerived : VirtualBase
{
void accept_crtp(const CRTPBase<A> &o) override { /* much logic to handle A */ }
void accept_crtp(const CRTPBase<B> &o) override { /* similar logic to handle B */ }
};
請注意CRTPBase<T>,在純虛基類及其所有派生類中, 的每個特化都有一個虛方法。這架空容易吹出來的比例與增加的專業化的數量CRTPBase<T>和更多的派生類VirtualBase。
我想做的大致如下:
struct VirtualBase
{
virtual ~VirtualBase() { }
template<typename T> virtual void accept_crtp(const CRTPBase<T> &o) = 0;
}
struct VirtualDerived : VirtualBase
{
template<typename T> void accept_crtp(const CRTPBase<T> &o) override {
/* one logic to handle any CRTPBase<T> */
}
};
由于開頭提到的原因,這是不可能的。用戶 Mark Essel 在另一篇 SO 帖子中遇到了同樣的問題(不過是在回答中,而不是在問題中)。用戶建議為每個特化宣告和定義虛擬方法,但在派生類中,在附加模板、非虛擬方法中實作實際邏輯,然后將呼叫從虛擬方法轉發到該模板方法:
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(const CRTPBase<A> &o) = 0;
virtual void accept_crtp(const CRTPBase<B> &o) = 0;
};
struct VirtualDerived : VirtualBase
{
void accept_crtp(const CRTPBase<A> &o) override { accept_any_crtp(o); }
void accept_crtp(const CRTPBase<B> &o) override { accept_any_crtp(o); }
private:
template<typename T>
void accept_any_crtp(const CRTPBase<T> &o) {
/* one logic to handle any CRTPBase<T> */
}
};
雖然這種方法避免了處理特化的邏輯代碼重復CRTPBase<T>,但它仍然需要在虛擬基類和所有派生類中為每個特化顯式撰寫一個方法。
我的問題是:如何減少實施開銷?
我已經考慮使用形式的X 宏
#define CRTP_SPECIALIZATIONS_LIST(X) X(A) X(B) // lists all specializations, here A and B
to generate the methods in the virtual base and derived classes. The problem with that is, if the CRTP hierarchy is defined in CRTP.hpp and the virtual base and derived classes are declared/defined in other source files, then the macro is "being leaked" by the header to all translation units that include it. Is there a more elegant way to solve this? Is there maybe a template way of achieving the same goal, perhaps with a variadic template type?
Your help is appreciated. Kind regards,
Immanuel
uj5u.com熱心網友回復:
如果您使用accept_crtp()所有委托給派生類的方法的不同多載撰寫 CRTP 基礎,則該派生類的方法可以是模板。該 CRTP 基礎也可用于實作虛擬基礎:
// declare virtual interface
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(const CRTPBase<A> &o) = 0;
virtual void accept_crtp(const CRTPBase<B> &o) = 0;
};
// implement virtual interface by delegating to derived class generic method
template<typename DerivedType>
struct CRTPDerived : VirtualBase
{
using derived_type = DerivedType;
virtual void accept_crtp(const CRTPBase<A> &o)
{ static_cast<derived_type*>(this)->accept_any_crtp(o); }
virtual void accept_crtp(const CRTPBase<B> &o)
{ static_cast<derived_type*>(this)->accept_any_crtp(o); }
};
// implement generic method
struct VirtualDerived : VirtualBase
{
private:
template<typename T>
void accept_any_crtp(const CRTPBase<T> &o) {
/* one logic to handle any CRTPBase<T> */
}
};
uj5u.com熱心網友回復:
由于所有型別都是已知的,您可能std::variant曾經擁有一個免費的訪問者實作:
using MyVariant =
std::variant<std::reference_wrapper<const CRTPBase<A>>,
std::reference_wrapper<const CRTPBase<B>>,
// ...
>
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(MyVariant) = 0;
};
struct VirtualDerived : VirtualBase
{
void accept_crtp(MyVariant var) override
{
std::visit([/*this*/](const auto& crtp){ /*...*/ }, var);
}
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/375294.html
