你如何断言一个对象在c++中有一个特定的方法

How do you assert that an object has a certain method in c++?

本文关键字:有一个 c++ 方法 一个对象 何断言 断言      更新时间:2023-10-16

如何断言一个对象在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;
}

这种方法比我以前的回答要简洁得多。

相关文章: