是否有可能编写一个模板,根据某个成员函数是否定义在类上而改变行为?
下面是我想写的一个简单的例子:
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表达式中推导()。
下面是工作代码的示例。
template<typename T>
using toStringFn = decltype(std::declval<const T>().toString());
template <class T, toStringFn<T>* = nullptr>
std::string optionalToString(const T* obj, int)
{
return obj->toString();
}
template <class T>
std::string optionalToString(const T* obj, long)
{
return "toString not defined";
}
int main()
{
A* a;
B* b;
std::cout << optionalToString(a, 0) << std::endl; // This is A
std::cout << optionalToString(b, 0) << std::endl; // toString not defined
}
toStringFn<T>* = nullptr将启用带有额外int参数的函数,该函数的优先级高于使用0调用时需要很长时间的函数。
你可以对函数使用相同的原则,如果函数被实现,返回true。
template <typename T>
constexpr bool toStringExists(long)
{
return false;
}
template <typename T, toStringFn<T>* = nullptr>
constexpr bool toStringExists(int)
{
return true;
}
int main()
{
A* a;
B* b;
std::cout << toStringExists<A>(0) << std::endl; // true
std::cout << toStringExists<B>(0) << std::endl; // false
}
我在另一个线程中对此写了一个答案(与上面的解决方案不同),也检查继承的成员函数:
SFINAE检查继承的成员函数
以下是该解决方案的一些例子:
例二:
我们正在检查一个具有以下签名的成员:
T::const_iterator begin(
template<class T> struct has_const_begin
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U const * data,
typename std::enable_if<std::is_same<
typename U::const_iterator,
decltype(data->begin())
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::type*)0));
};
请注意,它甚至检查方法的常量,并且也适用于基本类型。(我的意思是has_const_begin<int>::value为false,不会导致编译时错误。)
示例2
现在我们正在寻找签名:void foo(MyClass&, unsigned)
template<class T> struct has_foo
{
typedef char (&Yes)[1];
typedef char (&No)[2];
template<class U>
static Yes test(U * data, MyClass* arg1 = 0,
typename std::enable_if<std::is_void<
decltype(data->foo(*arg1, 1u))
>::value>::type * = 0);
static No test(...);
static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::type*)0));
};
请注意,MyClass不一定是默认可构造的或满足任何特殊的概念。该技术也适用于模板成员。
我急切地等待有关这方面的意见。
我一直在寻找一个方法,允许以某种方式不绑定结构名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;
}