你怎么断言一个对象在c ++中有某种方法?或者当你试图调用它时抛出一个异常而它不存在?这是我应该做些什么来处理我的问题?
我有一个容器类,里面有自定义对象。在容器方法中,我想调用'较小'对象的方法,但我需要确保它首先存在。
我可能不应该在这个类中使所需的方法成为纯虚函数,因为它也以不同的容器类以其他方式使用,而不需要它。正确?
答案 0 :(得分:1)
如果手头的特定对象存在方法,则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
,则可以使用类型检查,例如动态演员。假设一个类似于上面的示例,但在somethingSpecific
类的级别没有纯虚函数ContainerObject
:
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
)。
希望这有点帮助。
答案 1 :(得分:1)
如果可以编译您选择的表达式,则可以使用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;
如果true
是有效表达式,则会打印std::vector<int>().emplace_back(42.)
。
答案 2 :(得分:1)
另一种方法(来自Walter Brown:Part I,Part II)是使用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;
}
这种方法比我之前的答案简洁得多。