你如何断言一个对象在c++中有一个特定的方法
How do you assert that an object has a certain method in c++?
如何断言一个对象在c++中具有某个方法?或者当你试图调用它但它不存在时抛出异常?这是我应该做的事情来处理我的问题吗?
我有一个容器类,里面有一个自定义对象。在容器方法中,我想调用"较小"对象的方法,但我需要首先确保它存在。
我可能不应该在这个类中使所需的方法成为纯虚拟函数,因为不同的容器类也以其他方式使用它,而不需要这样。正确的
C++不提供运行时检查是否存在用于手头特定对象的方法。但您可以通过两种主要方式来克服这一问题:(1)在一些公共基类级别使用(纯)虚拟函数,或者(2)检查手头对象的类型。
(1)纯虚拟函数方法:
class ContainerObject {
...
virtual void somethingSpecific() = 0;
}
class MyContainerObject : public ContainerObject {
...
virtual void somethingSpecific() { ... }
}
因此,如果获得指向类型为ContainerObject
的对象的指针,则可以依赖于成员函数somethingSpecific()
的存在;注意,类ContainerObject
是抽象的,因为它包含一个纯虚拟函数,即一个没有实现的虚拟成员函数:
ContainerObject *o = someContainer.getSomeObject();
o->somethingSpecific(); // compiler checks existence of `somethingSpecific`.
(2)一种类型检查方法:
但是,如果您不想在一般级别公开somethingSpecific
,则可以使用类型检查,例如使用动态强制转换。假设一个类似于上面的例子,但没有ContainerObject
类级别的纯虚拟函数somethingSpecific
:
class ContainerObject {
...
virtual void anyOtherVirtualFunction();
}
class MyContainerObject : public ContainerObject {
...
virtual void somethingSpecific() { ... }
}
然后,基于动态转换的运行时类型检查尝试将getSomeObject
返回的对象解释为MyContainerObject
:
MyContainerObject *o = dynamic_cast<MyContainerObject*>(someContainer.getSomeObject());
if (o != nullptr) // does o point to a MyContainerObject?
o->somethingSpecific();
请注意,getSomeObject
可能返回除MyContainerObject
之外的对象。在这种情况下,动态强制转换的结果将为null。因此,如果结果不为null,那么您可以相信o
指向一个MyContainerObject
-实例(实现somethingSpecific
)。
进一步注意,动态强制转换要求多态性到位,这意味着基类ContainerObject
必须至少有一个虚拟成员函数(本例中为anyOtherVirtualFunction
)。
希望这能有所帮助。
如果您选择的表达式可以编译,则可以使用sfinae技巧编写静态成员值为true
的特性,如果不能编译,则为false
。我从2015年CppCon的一次演讲中得到了这个技巧,该演示称为
Fedor Pikus 的C++元编程/C++元编程
以下是链接:
https://www.youtube.com/watch?v=CZi6QqZSbFg
https://github.com/CppCon/CppCon2015/blob/master/Presentations/
以下是我对基于此技术的标头的看法:
#ifndef SU3_EXPRESSION_TRAITS_HH
#define SU3_EXPRESSION_TRAITS_HH
#include <type_traits>
// DEFINE_TRAIT from:
// https://www.youtube.com/watch?v=CZi6QqZSbFg
// https://github.com/CppCon/CppCon2015/blob/master/Presentations/
// C++ Metaprogramming/C++ Metaprogramming - Fedor Pikus - CppCon 2015.pdf
#define DEFINE_UNARY_TRAIT(NAME, EXPR)
template <typename T> struct NAME {
typedef char yes;
typedef char no[2];
template <typename U> static auto f(U&& x) -> decltype(EXPR, NAME::yes());
template <typename U> static no& f(...);
enum { value = sizeof(NAME::f<T>(std::declval<T>()))
== sizeof(NAME::yes) };
};
#define DEFINE_BINARY_TRAIT(NAME, EXPR)
template <typename T1, typename T2> struct NAME {
typedef char yes;
typedef char no[2];
template <typename U1, typename U2>
static auto f(U1&& x1, U2&& x2) -> decltype(EXPR, NAME::yes());
template <typename U1, typename U2> static no& f(...);
enum { value = sizeof(NAME::f<T1,T2>(std::declval<T1>(),std::declval<T2>()))
== sizeof(NAME::yes) };
};
#define DEFINE_VARIADIC_TRAIT(NAME, EXPR)
template <typename T, typename... TT> struct NAME {
typedef char yes;
typedef char no[2];
template <typename U, typename... UU>
static auto f(U&& x, UU&&... xx) -> decltype(EXPR, NAME::yes());
template <typename U, typename... UU> static no& f(...);
enum { value = sizeof(NAME::f<T,TT...>(std::declval<T>(),std::declval<TT>()...))
== sizeof(NAME::yes) };
};
namespace su3 {
DEFINE_UNARY_TRAIT(has_op_pre_increment, ++x)
DEFINE_UNARY_TRAIT(has_op_post_increment, x++)
DEFINE_UNARY_TRAIT(has_op_pre_decrement, --x)
DEFINE_UNARY_TRAIT(has_op_post_decrement, x--)
DEFINE_BINARY_TRAIT(has_op_plus_eq, x1+=x2)
DEFINE_BINARY_TRAIT(has_op_minus_eq, x1-=x2)
DEFINE_VARIADIC_TRAIT(is_callable, x(xx...))
DEFINE_VARIADIC_TRAIT(is_constructible, T(xx...))
} // end namespace
#endif
在包含标头的情况下,您可以使用DEFINE_*_TRAIT
宏之一为新表达式定义一个新特性。定义的特征可以这样使用:
std::cout << su3::has_op_pre_increment<int>::value << std::endl;
std::cout << su3::is_constructible<std::string,const char*>::value << std::endl;
或者检查包含的是否具有emplace_back()
成员功能,
DEFINE_VARIADIC_TRAIT(has_emplace_back, x.emplace_back(xx...))
std::cout << has_emplace_back<std::vector<int>,double>::value << std::endl;
如果std::vector<int>().emplace_back(42.)
是有效的表达式,这将打印true
。
另一种方法(来自Walter Brown:第一部分,第二部分)是使用void_t。
下面是一个检查是否可以调用emplace_back
成员函数的特性示例:
template <typename... T> struct make_void { typedef void type;};
template <typename... T> using void_t = typename make_void<T>::type;
template <typename, typename = void>
struct has_size : std::false_type { };
template <typename T>
struct has_size<T,
void_t<decltype( std::declval<T&>().size() )>
> : std::true_type { };
int main() {
std::cout << has_size<std::vector<int>>::value << std::endl;
}
这种方法比我以前的回答要简洁得多。
- 有一个打印语句的函数是一种糟糕的编程实践吗
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 有没有可能有一个只有ADL才能找到的非好友功能
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 我的程序有一个保存配置文件的GUI,如何双击此配置文件以直接加载带有配置数据的GUI?
- 在学习数据结构之前对STL有一个了解是好的吗?
- 我在 .h 中有一个枚举类,并且在.cpp错误中有一个运算符重载:与"运算符<<不匹配
- 如何在 Gnuplot 中分别绘制 2 个文件数据?我有一个文件"sin.txt",另一个文件"cos.txt",我想将它们分别绘制在一个图表上
- 是否可以在C++中有一个"generic"模板参数,该参数可以是非类型模板参数或类型?
- 我的输出中有一个额外的 0,为什么会这样
- 节点是否为空,即使它有一个值?
- 将数组信息存储到 c++ 向量中有一个"Access violation reading location"
- 在 Stream C++ 文本之前有一个额外的换行符
- 我可以有一个 ELI5 作为参考和指针以及何时使用它们吗?
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 我有一个类,它创建了另一个类的实例.如何将变量通过第一个类传递到第二个类的实例化中?
- 我有一个线程 1:EXC_BAD_ACCESS(代码 = 1,地址 = 0x8)错误.我认为这是由于内存管理不好.我可以
- 有一个构造函数,但有两个析构函数
- 我有一个关于C++提升的问题:: asio 和 std :: 异步