提问人:linuxbeginner 提问时间:10/18/2023 最后编辑:linuxbeginner 更新时间:10/18/2023 访问量:92
C++ - 在初始化成员类的条件下启用成员函数
C++ - enable usage of member functions under condition that member class was intialize
问:
假设我有一个不包含任何成员并执行计算的类:
class Helper{
public:
bool f1(TypeA a, TypeB b, Result& res){
// Some calculation requires TypeA, and TypeB and are assigned to the result
return true;
}
bool f2(TypeC c, Result& res){
// Some calculation requires TypeC and assign to the result
return true;
}
bool f3(TypeA a, TypeC c, Result& res){
// Some calculation requires TypeC and assign to the result
return true;
}
bool f4(Result& res){
// Some calculation that doesn't require anything and is assigned to the result
return true;
}
};
其中 TypeA、TypeB 和 TypeC 是一些类型。
只有当我有这些类型时,我才想使用辅助类,有些函数可能不需要任何成员类,有些只需要TypeC,有些需要两种类型等,所以我有以下类:
class Item{
public:
// Constructors, may or may not initialize some values
Item(){}
Item(TypeA a, TypeB b, TypeC c):a_(a),b_(b),c_(c){}
Item(TypeB b, TypeC c):b_(b),c_(c){}
// usage of member functions
bool f1(Result& res){
if (a_.has_value() && b_.has_value())
{
return h.f1(a_.value(),b_.value(),res);
}
// runtime error
throw std::logic_error("some msg");
}
bool f2(Result& res){
// Allow only if c was initiated
if (c_.has_value())
{
return h.f2(c_.value(), res);
}
// runtime error
throw std::logic_error("some msg");
}
bool f4(Result& res){
// Doesn't require any member class
return h.f4(res);
}
private:
std::optional<TypeA> a_;
std::optional<TypeB> b_;
std::optional<TypeC> c_;
Helper h;
};
因此,只有当用户正确使用它并启动所需的成员时,他们才能期望具有 Helper 功能。但这是一个运行时错误,而不是编译时错误。所以我想也许只有在满足条件的情况下我才能允许编译。
我正在研究这个线程:c++ 启用、禁用类成员函数?
但这可能会导致代码重复(因为我需要将每个函数复制到这些类中),并且不可维护,因为对于每个添加的函数,我需要问自己“哪些类可以使用此函数?那么也许有更好的解决方案?我很想听听你的想法。
答:
添加一个额外的模板参数 T,其默认值为要检查的类型(TypeA 或 TypeC) 使用 std::enable_if 和 std::is_same 仅在 T 与所需类型匹配时启用该函数 现在,仅当 TypeA 存在时才启用 f1,仅当 TypeC 存在时才启用 f2,依此类推。
#include <type_traits>
#include <optional>
class Helper {
// Same as before
};
class Item {
public:
template <typename T = TypeA,
typename = std::enable_if_t<std::is_same_v<T, TypeA>>>
bool f1(TypeA a, TypeB b, Result& res) {
return h.f1(a, b, res);
}
template <typename T = TypeC,
typename = std::enable_if_t<std::is_same_v<T, TypeC>>>
bool f2(TypeC c, Result& res) {
return h.f2(c, res);
}
bool f4(Result& res) {
return h.f4(res);
}
private:
std::optional<TypeA> a_;
std::optional<TypeB> b_;
std::optional<TypeC> c_;
Helper h;
};
评论
我建议在使用这三个初始化时不要使用相同的类型,并且不要使用所有三个初始化时相同的类型 - 或者仅使用.它们不共享公共接口,也不携带相同的数据。我会做一个班级模板。Item
TypeA
TypeB
TypeC
Item
Item
在这里,我使用 a 作为私有基类来存储所提供类型的值。std::tuple
例:
#include <tuple>
#include <type_traits>
// Helper trait to check if a type is any of the supplied types
template <class T, class... Ts>
inline constexpr bool is_any_of_v = std::disjunction_v<std::is_same<T, Ts>...>;
template <class... Ts>
class Item : private std::tuple<Ts...> {
public:
using std::tuple<Ts...>::tuple;
using std::tuple<Ts...>::operator=;
template<class A = TypeA, class B = TypeB>
std::enable_if_t<is_any_of_v<A, Ts...> && is_any_of_v<B, Ts...>, bool>
f1(Result& res) {
return Helper::f1(std::get<TypeA>(*this), std::get<TypeB>(*this), res);
}
template<class C = TypeC>
std::enable_if_t<is_any_of_v<C, Ts...>, bool>
f2(Result& res) {
return Helper::f2(std::get<TypeC>(*this), res);
}
bool f4(Result& res) {
// Doesn't require any member class
return Helper::f4(res);
}
};
template <class... Ts>
Item(Ts...) -> Item<std::remove_cvref_t<Ts>...>;
注意:我在这里将您的函数放在 .Helper
namespace
然后可以这样使用它:
int main() {
TypeA a;
TypeB b;
Result r;
Item item(a, b);
item.f1(r);
// item.f2(r); // compilation error, has no TypeC so f2() doesn't exist
}
item
在上面的例子中,be 的类型与 an 或 etc 没有任何关系。您供应的每个独特的类型组合都将产生一个唯一的类型。Item<TypeA, TypeB>
Item<TypeC>
Item<TypeB, TypeA>
Item
使用 C++20,您可以将 s 替换为子句:
演示由 Miles Budnek 友情提供std::enable_if_t
requires
评论
Helper
看起来它应该是一个而不是一个.namespace
class
virtual
namespace
Item
Item i{}; if (rand() % 2 == 0) i = Item{a, b, c}; f1(res);
rand()