是否有可能编写一个模板,根据某个成员函数是否定义在类上而改变行为?
下面是我想写的一个简单的例子:
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”部分。
下面是工作代码的示例。
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
}
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个字母)
泛型模板,用于检查类型是否支持某些“特性”:
#include <type_traits>
template <template <typename> class TypeChecker, typename Type>
struct is_supported
{
// these structs are used to recognize which version
// of the two functions was chosen during overload resolution
struct supported {};
struct not_supported {};
// this overload of chk will be ignored by SFINAE principle
// if TypeChecker<Type_> is invalid type
template <typename Type_>
static supported chk(typename std::decay<TypeChecker<Type_>>::type *);
// ellipsis has the lowest conversion rank, so this overload will be
// chosen during overload resolution only if the template overload above is ignored
template <typename Type_>
static not_supported chk(...);
// if the template overload of chk is chosen during
// overload resolution then the feature is supported
// if the ellipses overload is chosen the the feature is not supported
static constexpr bool value = std::is_same<decltype(chk<Type>(nullptr)),supported>::value;
};
检查方法foo是否与signature double兼容的模板(const char*)
// if T doesn't have foo method with the signature that allows to compile the bellow
// expression then instantiating this template is Substitution Failure (SF)
// which Is Not An Error (INAE) if this happens during overload resolution
template <typename T>
using has_foo = decltype(double(std::declval<T>().foo(std::declval<const char*>())));
例子
// types that support has_foo
struct struct1 { double foo(const char*); }; // exact signature match
struct struct2 { int foo(const std::string &str); }; // compatible signature
struct struct3 { float foo(...); }; // compatible ellipsis signature
struct struct4 { template <typename T>
int foo(T t); }; // compatible template signature
// types that do not support has_foo
struct struct5 { void foo(const char*); }; // returns void
struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double
struct struct7 { double foo( int *); }; // const char* can't be converted to int*
struct struct8 { double bar(const char*); }; // there is no foo method
int main()
{
std::cout << std::boolalpha;
std::cout << is_supported<has_foo, int >::value << std::endl; // false
std::cout << is_supported<has_foo, double >::value << std::endl; // false
std::cout << is_supported<has_foo, struct1>::value << std::endl; // true
std::cout << is_supported<has_foo, struct2>::value << std::endl; // true
std::cout << is_supported<has_foo, struct3>::value << std::endl; // true
std::cout << is_supported<has_foo, struct4>::value << std::endl; // true
std::cout << is_supported<has_foo, struct5>::value << std::endl; // false
std::cout << is_supported<has_foo, struct6>::value << std::endl; // false
std::cout << is_supported<has_foo, struct7>::value << std::endl; // false
std::cout << is_supported<has_foo, struct8>::value << std::endl; // false
return 0;
}
http://coliru.stacked-crooked.com/a/83c6a631ed42cea4