如何结合切片、模板和非导出类?
How to combine slicing, templates and non derived classes?
我想要一个可以摄取对象的容器,并将对象的不同部分存储到不同的内部容器中。
像这样:
// Pseudo-code
template<typename A, typename B, typename C>
class Container {
std::vector<B> vb;
std::vector<C> vc;
public:
void push_back(const A &a) {
vb.push_back(a);
vc.push_back(a);
}
};
class AA {
int a;
std::string s;
};
class BB {
int a;
};
class CC {
std::string s;
};
Container<AA, BB, CC> cont;
AA aa;
cont.push_back(aa);
我希望对象 vb
获得 类 A 和 B(切片)之间的共同点,对象 vc
.
也是如此
理想情况下,类AA、BB、CC不应该是等级关系。我希望编译器能够按类型匹配成员。
我更喜欢性能损失较小的解决方案。
您可以使用标签调度 对类型进行分类并有条件地挑选其成员。一个例子:
template<class T> typename std::is_same<decltype(T::s), std::string>::type test_has_string_s(int);
template<class T> std::false_type test_has_string_s(...);
template<class T> using HasStringS = decltype(test_has_string_s<T>(0));
template<class T> typename std::is_same<decltype(T::a), int>::type test_has_int_a(int);
template<class T> std::false_type test_has_int_a(...);
template<class T> using HasIntA = decltype(test_has_int_a<T>(0));
template<typename B, typename C>
class Container {
std::vector<B> vb;
std::vector<C> vc;
template<class T> void push_back_vb(T const& t, std::true_type) { vb.push_back(B{t.s}); }
template<class T> void push_back_vb(T const&, std::false_type) {}
template<class T> void push_back_vc(T const& t, std::true_type) { vc.push_back(C{t.a}); }
template<class T> void push_back_vc(T const&, std::false_type) {}
public:
template<class T>
void push_back(T const& t) {
static_assert(HasStringS<T>::value || HasIntA<T>::value, "Must have any of {T::a, T::s}.");
push_back_vb(t, HasStringS<T>{});
push_back_vc(t, HasIntA<T>{});
}
};
struct AA {
int a;
std::string s;
};
struct BB {
std::string s;
};
struct CC {
int a;
};
struct DD {};
int main() {
Container<BB, CC> cont;
cont.push_back(AA{});
cont.push_back(BB{});
cont.push_back(CC{});
cont.push_back(DD{}); // error: static assertion failed: Must have any of {T::a, T::s}.
}
您的伪代码容器已经在 AA
继承 BB
和 CC
的情况下工作,因为切片允许从派生到基的隐式转换。但是,您的实施并不 限于 这种情况。所需要的只是 AA
可以隐式转换为 BB
和 CC
。继承就足够了,但不是必需的。
您可以通过定义转换运算符使 class 可转换为其他对象:
struct AA {
operator BB() const { return {a}; }
operator CC() const { return {s}; }
int a;
std::string s;
};
或者,如果您希望依赖关系以另一种方式进行,则可以将转换构造函数添加到 BB
and/or CC
:
struct BB {
BB(const AA& aa) : a(aa.a) {}
int a;
};
但是请注意,像这样缩小隐式转换可能会导致某些错误被忽视。
我想要一个可以摄取对象的容器,并将对象的不同部分存储到不同的内部容器中。
像这样:
// Pseudo-code
template<typename A, typename B, typename C>
class Container {
std::vector<B> vb;
std::vector<C> vc;
public:
void push_back(const A &a) {
vb.push_back(a);
vc.push_back(a);
}
};
class AA {
int a;
std::string s;
};
class BB {
int a;
};
class CC {
std::string s;
};
Container<AA, BB, CC> cont;
AA aa;
cont.push_back(aa);
我希望对象 vb
获得 类 A 和 B(切片)之间的共同点,对象 vc
.
理想情况下,类AA、BB、CC不应该是等级关系。我希望编译器能够按类型匹配成员。
我更喜欢性能损失较小的解决方案。
您可以使用标签调度 对类型进行分类并有条件地挑选其成员。一个例子:
template<class T> typename std::is_same<decltype(T::s), std::string>::type test_has_string_s(int);
template<class T> std::false_type test_has_string_s(...);
template<class T> using HasStringS = decltype(test_has_string_s<T>(0));
template<class T> typename std::is_same<decltype(T::a), int>::type test_has_int_a(int);
template<class T> std::false_type test_has_int_a(...);
template<class T> using HasIntA = decltype(test_has_int_a<T>(0));
template<typename B, typename C>
class Container {
std::vector<B> vb;
std::vector<C> vc;
template<class T> void push_back_vb(T const& t, std::true_type) { vb.push_back(B{t.s}); }
template<class T> void push_back_vb(T const&, std::false_type) {}
template<class T> void push_back_vc(T const& t, std::true_type) { vc.push_back(C{t.a}); }
template<class T> void push_back_vc(T const&, std::false_type) {}
public:
template<class T>
void push_back(T const& t) {
static_assert(HasStringS<T>::value || HasIntA<T>::value, "Must have any of {T::a, T::s}.");
push_back_vb(t, HasStringS<T>{});
push_back_vc(t, HasIntA<T>{});
}
};
struct AA {
int a;
std::string s;
};
struct BB {
std::string s;
};
struct CC {
int a;
};
struct DD {};
int main() {
Container<BB, CC> cont;
cont.push_back(AA{});
cont.push_back(BB{});
cont.push_back(CC{});
cont.push_back(DD{}); // error: static assertion failed: Must have any of {T::a, T::s}.
}
您的伪代码容器已经在 AA
继承 BB
和 CC
的情况下工作,因为切片允许从派生到基的隐式转换。但是,您的实施并不 限于 这种情况。所需要的只是 AA
可以隐式转换为 BB
和 CC
。继承就足够了,但不是必需的。
您可以通过定义转换运算符使 class 可转换为其他对象:
struct AA {
operator BB() const { return {a}; }
operator CC() const { return {s}; }
int a;
std::string s;
};
或者,如果您希望依赖关系以另一种方式进行,则可以将转换构造函数添加到 BB
and/or CC
:
struct BB {
BB(const AA& aa) : a(aa.a) {}
int a;
};
但是请注意,像这样缩小隐式转换可能会导致某些错误被忽视。