是否可以编写一个模板,根据是否在类上定义了某个成员函数来改变行为?
下面是我想写的一个简单的例子:
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”部分。
是的,使用SFINAE,您可以检查给定的类是否提供了某个方法。工作代码如下:
#include <iostream>
struct Hello
{
int helloworld() { return 0; }
};
struct Generic {};
// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
struct two { char x[2]; };
template <typename C> static one test( decltype(&C::helloworld) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}
我刚刚用Linux和GCC4.1/4.3对它进行了测试。我不知道它是否可移植到运行不同编译器的其他平台。
这个问题是老问题,但是在C++11中,我们有了一种新的方法来检查函数的存在性(或者任何非类型成员的存在性,真的),再次依赖于SFINAE:
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
-> decltype(os << obj, void())
{
os << obj;
}
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
-> decltype(obj.stream(os), void())
{
obj.stream(os);
}
template<class T>
auto serialize(std::ostream& os, T const& obj)
-> decltype(serialize_imp(os, obj, 0), void())
{
serialize_imp(os, obj, 0);
}
现在来解释一下。首先,如果decltype
中的第一个表达式无效(也就是函数不存在),我使用表达式SFINAE从重载解析中排除serialize(_imp)
函数。
void()
用于使所有这些函数的返回类型void
。
0
参数用于在os<
0
是int
类型,因此第一个重载更匹配)。
现在,您可能需要一个特征来检查函数是否存在。幸运的是,写这个很容易。但是,请注意,您需要为每一个不同的函数名自己编写一个特性。
#include <type_traits>
template<class>
struct sfinae_true : std::true_type{};
namespace detail{
template<class T, class A0>
static auto test_stream(int)
-> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
template<class, class A0>
static auto test_stream(long) -> std::false_type;
} // detail::
template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};
活生生的例子。
接着解释。首先,sfinae_true
是一个帮助器类型,它基本上等同于编写decltype(void(std::declval
。其优点只是它更短。
其次,结构has_stream:decltype(...)
最终继承自std::true_type
或std::false_type
,这取决于test_stream
中的decltype
检查是否失败。
最后,std::declval
给出了您传递的任何类型的“值”,而不需要知道如何构造它。请注意,这只能在未求值的上下文中实现,如decltype
、sizeof
等。
注意,不一定需要decltype
,因为sizeof
(以及所有未求值的上下文)得到了这种增强。只是decltype
已经提供了一个类型,因此更加简洁。以下是其中一个重载的sizeof
版本:
template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
int(*)[sizeof((os << obj),0)] = 0)
{
os << obj;
}
由于相同的原因,int
和long
参数仍然存在。数组指针用于提供可以使用sizeof
的上下文。