你怎么断言一个对象在c ++中有某种方法?

时间:2017-01-13 20:40:17

标签: c++ oop c++11 exception-handling assertions

你怎么断言一个对象在c ++中有某种方法?或者当你试图调用它时抛出一个异常而它不存在?这是我应该做些什么来处理我的问题?

我有一个容器类,里面有自定义对象。在容器方法中,我想调用'较小'对象的方法,但我需要确保它首先存在。

我可能不应该在这个类中使所需的方法成为纯虚函数,因为它也以不同的容器类以其他方式使用,而不需要它。正确?

3 个答案:

答案 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 IPart 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;
}

这种方法比我之前的答案简洁得多。