是否有可能编写一个模板,根据某个成员函数是否定义在类上而改变行为?
下面是我想写的一个简单的例子:
template<class T>
std::string optionalToString(T* obj)
{
if (FUNCTION_EXISTS(T->toString))
return obj->toString();
else
return "toString not defined";
}
因此,如果类T定义了toString(),那么它就使用它;否则,它就不会。我不知道如何做的神奇部分是“FUNCTION_EXISTS”部分。
如果方法恰好定义在基类中,那么这里由litb提供的标准c++解决方案将不能像预期的那样工作。
处理这种情况的解决方案请参考:
俄语:
http://www.rsdn.ru/forum/message/2759773.1.aspx
由罗马人翻译的英文。Perepelitsa:
http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1
它非常聪明。然而,这种解决方案的一个问题是,如果被测试的类型不能用作基类(例如基本类型),则会给出编译器错误。
在Visual Studio中,我注意到如果使用没有参数的方法,则需要在参数周围插入一对额外的冗余()来在sizeof表达式中推导()。
这里有很多答案,但我没有找到一个版本,它执行真正的方法解析排序,同时不使用任何较新的c++特性(只使用c++98特性)。
注意:此版本已测试,并使用vc++2013, g++ 5.2.0和在线编译器。
所以我提出了一个版本,只使用sizeof():
template<typename T> T declval(void);
struct fake_void { };
template<typename T> T &operator,(T &,fake_void);
template<typename T> T const &operator,(T const &,fake_void);
template<typename T> T volatile &operator,(T volatile &,fake_void);
template<typename T> T const volatile &operator,(T const volatile &,fake_void);
struct yes { char v[1]; };
struct no { char v[2]; };
template<bool> struct yes_no:yes{};
template<> struct yes_no<false>:no{};
template<typename T>
struct has_awesome_member {
template<typename U> static yes_no<(sizeof((
declval<U>().awesome_member(),fake_void()
))!=0)> check(int);
template<typename> static no check(...);
enum{value=sizeof(check<T>(0)) == sizeof(yes)};
};
struct foo { int awesome_member(void); };
struct bar { };
struct foo_void { void awesome_member(void); };
struct wrong_params { void awesome_member(int); };
static_assert(has_awesome_member<foo>::value,"");
static_assert(!has_awesome_member<bar>::value,"");
static_assert(has_awesome_member<foo_void>::value,"");
static_assert(!has_awesome_member<wrong_params>::value,"");
现场演示(带有扩展的返回类型检查和vc++2010解决方案):http://cpp.sh/5b2vs
没有消息来源,因为是我自己想出来的。
在g++编译器上运行Live演示时,请注意数组大小为0是允许的,这意味着使用static_assert将不会触发编译器错误,即使它失败了。
一个常用的解决方法是将宏中的'typedef'替换为'extern'。
pre -c++20,简单用例的简单选项:
如果你知道你的类是默认可构造的,我们可以使语法更简单。
我们将从最简单的情况开始:默认可构造对象,并且我们知道预期的返回类型。实例方法:
int foo ();
我们可以写出没有declval的类型trait:
template <auto v>
struct tag_v
{
constexpr static auto value = v;
};
template <class, class = int>
struct has_foo_method : tag_v<false> {};
template <class T>
struct has_foo_method <T, decltype(T().foo())>
: tag_v<true> {};
demo
注意,我们将默认类型设置为int,因为这是foo的返回类型。
如果有多个可接受的返回类型,则向decltype添加第二个参数,该参数与默认类型相同,覆盖第一个参数:
decltype(T().foo(), int())
demo
(这里的int类型不重要-我使用它是因为它只有3个字母)
我一直在寻找一个方法,允许以某种方式不绑定结构名has_member类的成员的名字。
实际上,如果lambda可以被允许在未求值的表达式中(这是被标准禁止的),这将更简单,即has_member<ClassName, SOME_MACRO_WITH_DECLTYPE(member_name)>
#include <iostream>
#include <list>
#include <type_traits>
#define LAMBDA_FOR_MEMBER_NAME(NAME) [](auto object_instance) -> decltype(&(decltype(object_instance)::NAME)) {}
template<typename T>
struct TypeGetter
{
constexpr TypeGetter() = default;
constexpr TypeGetter(T) {}
using type = T;
constexpr auto getValue()
{
return std::declval<type>();
}
};
template<typename T, typename LambdaExpressionT>
struct has_member {
using lambda_prototype = LambdaExpressionT;
//SFINAE
template<class ValueT, class = void>
struct is_void_t_deducable : std::false_type {};
template<class ValueT>
struct is_void_t_deducable<ValueT,
std::void_t<decltype(std::declval<lambda_prototype>()(std::declval<ValueT>()))>> : std::true_type {};
static constexpr bool value = is_void_t_deducable<T>::value;
};
struct SimpleClass
{
int field;
void method() {}
};
int main(void)
{
const auto helpful_lambda = LAMBDA_FOR_MEMBER_NAME(field);
using member_field = decltype(helpful_lambda);
std::cout << has_member<SimpleClass, member_field>::value;
const auto lambda = LAMBDA_FOR_MEMBER_NAME(method);
using member_method = decltype(lambda);
std::cout << has_member<SimpleClass, member_method>::value;
}