是否有可能编写一个模板,根据某个成员函数是否定义在类上而改变行为?
下面是我想写的一个简单的例子:
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 <class T>
struct has_foo
{
struct S { void foo(...); };
struct derived : S, T {};
template <typename V, V> struct W {};
template <typename X>
char (&test(W<void (X::*)(), &X::foo> *))[1];
template <typename>
char (&test(...))[2];
static const bool value = sizeof(test<derived>(0)) == 1;
};
你必须确保T是一个类。查找foo时的模糊性似乎是替换失败。我让它在gcc上工作,但不确定它是否是标准的。
我也遇到过类似的问题:
一个模板类,可以从少数基类派生,其中一些基类具有某个成员,而另一些基类没有。
我解决它类似于“typeof”(Nicola Bonelli)的答案,但使用decltype,所以它在MSVS上编译和正确运行:
#include <iostream>
#include <string>
struct Generic {};
struct HasMember
{
HasMember() : _a(1) {};
int _a;
};
// SFINAE test
template <typename T>
class S : public T
{
public:
std::string foo (std::string b)
{
return foo2<T>(b,0);
}
protected:
template <typename T> std::string foo2 (std::string b, decltype (T::_a))
{
return b + std::to_string(T::_a);
}
template <typename T> std::string foo2 (std::string b, ...)
{
return b + "No";
}
};
int main(int argc, char *argv[])
{
S<HasMember> d1;
S<Generic> d2;
std::cout << d1.foo("HasMember: ") << std::endl;
std::cout << d2.foo("Generic: ") << std::endl;
return 0;
}
泛型模板,用于检查类型是否支持某些“特性”:
#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