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

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

在c++中如何断言一个对象有某个方法?或者当您尝试调用它但它不存在时抛出异常?这是我应该做的来处理我的问题吗?

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

我可能不应该在此 class 中将所需的方法设为纯虚函数,因为它也被不需要它的不同容器 class 以其他方式使用这边走。对吗?

C++ 不提供 runtime-check 如果手头的特定对象存在方法。但是您可以通过两种主要方式克服这个问题:(1) 在某些公共基础 class 级别使用(纯)虚函数,或者 (2) 检查手头对象的类型。

(1)纯虚函数方法:

class ContainerObject {
  ...
  virtual void somethingSpecific() = 0;
}
class MyContainerObject : public ContainerObject {
  ...
  virtual void somethingSpecific() {  ... }
}

因此,如果你得到一个指向ContainerObject类型对象的指针,你可以依赖成员函数somethingSpecific()的存在;请注意 class ContainerObject 是抽象的,因为它包含一个纯虚函数,即没有实现的虚成员函数:

ContainerObject *o = someContainer.getSomeObject();
o->somethingSpecific(); // compiler checks existence of `somethingSpecific`.

(2)一种类型检查的方法:

但是,如果您不想在一般级别公开 somethingSpecific,您可以使用类型检查,例如动态演员表。假设一个与上面类似的例子,但是在 ContainerObject class:

级别没有纯虚函数 somethingSpecific
class ContainerObject {
  ...
  virtual void anyOtherVirtualFunction();
}
class MyContainerObject : public ContainerObject {
  ...
  virtual void somethingSpecific() {  ... }
}

然后基于动态转换的运行时类型检查尝试将由 getSomeObject 编辑的对象 return 解释为 MyContainerObject

MyContainerObject *o = dynamic_cast<MyContainerObject*>(someContainer.getSomeObject());
if (o != nullptr)  // does o point to a MyContainerObject?
  o->somethingSpecific(); 

请注意 getSomeObject 可能 return 不是 MyContainerObject 的对象。在这种情况下,动态转换的结果将为空。因此,如果结果不为空,那么您可以依赖 o 指向 MyContainerObject 实例(实现 somethingSpecific)。

进一步注意,动态转换要求多态性到位,这意味着基础 class ContainerObject 必须至少有一个虚拟成员函数(本例中的 anyOtherVirtualFunction ).

希望对您有所帮助。

您可以使用 sfinae 技巧编写一个具有静态成员值的特征,如果您选择的表达式可以编译,则为 true,如果不能,则为 false。 我从 2015 CppCon 的演讲中学到了这个技巧, 该演示文稿名为

C++ Metaprogramming/C++ Metaprogramming by Fedor Pikus

链接如下:

https://www.youtube.com/watch?v=CZi6QqZSbFg

https://github.com/CppCon/CppCon2015/blob/master/Presentations/

这是我基于此技术对 header 的看法:

#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

包含 header 后,您可以使用 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:Part I, Part II) is to use 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;
}

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