如果我想讓一個類有一個可選成員,我就使用模板專業化:
如果我想讓一個類有一個可選成員,我就使用模板專業化。
template<class T>
struct X {
T t;
void print() { cout < < "t是" << t << '
};
模板<>
struct X<void> {
void print() { cout<< "without T
"; };;;
};
這很好,因為沒有運行時的開銷,也沒有什么代碼重復。 但是,如果我有3個而不是1個可選的類成員,我就必須撰寫2^3=8個類,也就是說,"少量重復 "很快就變得不合理了。
一個可能的解決方案可能是使用std::conditional,像這樣:
template< class T1。class T2, class T3>
struct X {
conditional_t<is_void_v<T1>, char, T1> t1。
conditional_t<is_void_v<T2>, char, T2> t2。
conditional_t<is_void_v<T3>, char, T3> t3;
void print() {
if constexpr (!is_void_v<T1>) cout << "t1 is " << t1 << '
';
if constexpr (!is_void_v<T2>) cout << "t2 is " << t2 <<'
';
if constexpr (! is_void_v<T3>) cout << "t3 is " <<t3 <<'
';
}
};
但是現在,我的類的物件浪費記憶體。我正在尋找一些方法來避免大部分的代碼重復(最多下降到可選成員數量的線性代碼開銷),同時避免花費超過必要的運行時間和記憶體。
請注意,對于單個可選的類成員,這個問題是存在的(有答案)(見沒有運行時開銷的可選類成員,在 C 中實作基于模板的可選類成員的最有效方法?),但據我所知,對于多個可選的成員,這個問題還沒有答案。
uj5u.com熱心網友回復:
對于一個optional_member類,
template <class T>。
struct OptionalMember
{
T t;
static constexpr bool has_member = true;
};
template<> struct X<void>
{
static constexpr bool has_member = false;
};
你可以使用繼承(只要它們的型別不同)和EBO來避免額外的記憶體。
template< class T1, class T2, class T3>
struct X : OptionalMember<T1> , OptionalMember<T2> , OptionalMember<T3>
{
void print() {
if constexpr (!OptionalMember<T1> ::have_member)
cout << "t1是" << OptionalMember<T1>:t <<'
'。
if constexpr (!OptionalMember<T2> ::have_member)
cout << "t2是" << OptionalMember<T2> :t << '
'。
if constexpr (!OptionalMember< T1> ::has_member)
cout << "t3是" << OptionalMember<T3> :t << '
'。
}
};
或者,從C 20開始,屬性[[no_unique_address]](只要它們的型別不同就沒有額外的記憶體)。
template< class T1, class T2, class T3>
struct X {
[[no_unique_address]]。OptionalMember<T1> t1。
[[no_unique_address]]可選成員<T2> t1;。OptionalMember<T2> t2。
[[no_unique_address]] 可選會員<T3 OptionalMember<T3> t3。
void print() {
if constexpr (!t1.has_member) cout << "t1 is " << t1.t << ''
'。
if constexpr (!t2.has_member) cout << "t2 is " << t2.t <<'
'。
if constexpr (!t3.has_member) cout << "t3 is " << t3.t <<'
'。
}
};
如果型別可能是相同的,你可以修改OptionalMember以接受額外的標簽(或任何種類的識別符號作為std::size_t):
template < class T, class Tag>
struct OptionalMember >。
{
T t;
static constexpr bool has_member = true;
};
template<class Tag> struct X<void, Tag>
{
static constexpr bool has_member = false;
};
然后
struct tag1;
struct tag2;
struct tag3;
并且使用OptionalMember<TX, tagX>代替上面解決方案中的OptionalMember<TX>。
uj5u.com熱心網友回復:
可以使用繼承來只寫2*3=6個類,并將資料與使用它的實際代碼分開:
template<class T1> struct optional_tuple1 {T1 first;};
template<> struct optional_tuple1<void> {};
template<class T1, class T2 > struct optional_tuple2: public optional_tuple1<T1> { T2 second;}。
template<class T1> struct optional_tuple2<T1,void> 。public optional_tuple1<T1> {}。
模板<。 class T1, class T2, class T3> struct optional_tuple3: public optional_tuple2<T1, T2> {T3 third;};
template<class T1, class T2 > struct optional_tuple3<T1, T2, void> 。public optional_tuple2<T1, T2> {}。
然而,你必須確保,如果T2是void,你就不會使用變數second。這可以通過if constexpr (!std::is_void_v<T2>)(如上)實作:
template< class T1。class T2, class T3>
struct X3: public optional_tuple3< T1, T2, T3> {
void print() {
if constexpr (! is_void_v<T1>) cout << "first is " << this->first << '
';
if constexpr (! is_void_v<T2>) cout << "second is " << this->second << '
';
if constexpr (! is_void_v<T3>) cout << "第三是" <<this->第三 <<'
';
}
};
注意:只有在不是所有的T都是void的情況下,這才是最理想的,因為如果我沒有記錯的話,在C 中類的大小不能為0(我假設是因為沒有兩個物件可以在記憶體中接收相同的地址)。
uj5u.com熱心網友回復:
像這樣的東西怎么樣:
template<typename T>
struct member_haver {
T m_member。
};
template<>
struct member_haver<void> {
};
template<typename。Ts>
struct X : member_haver< Ts> ... {
void print() {
((std::cout << member_haver<Ts>:m_member << ", "), ...) 。
}
};
然后,例如,你可以說:
int main(){
X<int, void, float> x{};
x.member_haver<int>::m_member = 1;
x.member_haver<float> ::m_member = 5.2f;
x.print()。
return 0;
當然,這可能需要在語法上進行改進,但要點是存在的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/329616.html
標籤:
