你如何断言一个对象在 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;
}
这种方法比我之前回答中的方法简洁得多。
在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;
}
这种方法比我之前回答中的方法简洁得多。