[英]What is the best way to create a 'using' declaration involving members of incomplete types?
我有一個非常簡單的 CRTP 骨架結構,它在基類中只包含一個向量和一個私有訪問器。 CRTP 類中有一個輔助方法可以訪問它。
#include <vector>
template<typename T>
class CRTPType {
// Will be used by other member functions. Note it's private,
// so declval/decltype expressions outside of friends or this class
// cannot see it
auto& _v() {
return static_cast<T *>(this)->getV();
}
};
class BaseType : public CRTPType<BaseType> {
friend class CRTPType<BaseType>;
std::vector<int> v;
//For different base types this impl will vary
auto& getV() { return v; }
};
到目前為止,一切都很好。 現在我想向CRTPType
添加一個using
聲明,這將是_v()
返回的類型。 因此,理想情況下,可以執行以下操作:
template<typename T>
class CRTPType {
//Will be used by other member functions
auto& _v() {
return static_cast<T *>(this)->getV();
}
using vtype = decltype(std::declval<CRTPType<T>>()._v());
};
問題是類型不完整,所以我不能在CRTPType
中使用任何decltype
/ declval
表達式。
有沒有一種干凈的方法可以盡可能少地破壞封裝? 理想情況下在 C++14 中,但如果有任何新的語言功能有幫助,我會很感興趣。
如果您不太關心using
聲明的出現位置和准確方式,那么您可以在不做太多更改的情況下避免該問題,例如將using
聲明放入嵌套類中:
template<typename T>
class CRTPType {
//Will be used by other member functions
auto& _v() {
return static_cast<T *>(this)->getV();
}
struct nested {
using vtype = decltype(std::declval<CRTPType<T>>()._v());
};
};
原始代碼中的問題是using
聲明是使用類模板專業化隱式實例化的, CRTPType<BaseType>
的隱式實例化點在BaseType
的定義之前,因為后者使用前者作為基類(這是必需的是完整的,因此被隱式實例化)。
另一方面,嵌套成員類被指定為不使用類模板特化隱式實例化,而是僅在需要它們完成時才進行實例化。 換句話說, using
聲明的實例化點現在將緊接在命名空間范圍聲明之前,該聲明實際上直接或間接地使用nested::vtype
。
另一種選擇是使using
聲明成為模板:
template<typename T>
class CRTPType {
//Will be used by other member functions
auto& _v() {
return static_cast<T *>(this)->getV();
}
template<typename U = T>
using vtype = decltype(std::declval<CRTPType<U>>()._v());
};
成員模板也不能使用包含類模板特化來隱式實例化。 可能需要使用U = T
構造,並且僅在using =
中使用U
原因是如果右側的類型不依賴於模板參數,編譯器可以在模板定義后立即檢查是否可以實例化,這正是我們想要的不可能避免。 因此,該程序可能是格式錯誤的,如果只使用T
,則不需要診斷。 (我真的不是 100% 確定這適用於此,但 Clang 確實在抱怨。)
另一種可能性是將using
聲明移到類之外,在這種情況下,很明顯它不會被隱式實例化:
template<typename T>
class CRTPType;
template<typename T>
using CRTPType_vtype = decltype(std::declval<CRTPType<T>>()._v());
template<typename T>
class CRTPType {
//Will be used by other member functions
auto& _v() {
return static_cast<T *>(this)->getV();
}
};
使用嵌套類或命名空間的變體與封閉命名空間中的using
聲明相結合,可以從外部隱藏附加名稱。
有了上述所有內容,您仍然需要注意CRTPType<BaseType>
的實例化或BaseType
的定義中沒有其他任何內容實際上間接使用vtype
。 如果發生這種情況,您可能會回到最初的問題,甚至可能取決於成員的聲明順序(盡管從技術上講這不是編譯器的標准符合行為)。
在任何情況下,您都需要將 BaseType 中的CRTPType<BaseType>
BaseType
friend
,或者將BaseType
中的getV
標記為public
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.