是否可以编写一个模板,根据类中是否定义了某个成员函数来更改行为?
下面是我想要写的一个简单的例子:
template<class T>
std::string optionalToString(T* obj)
{
if (FUNCTION_EXISTS(T->toString))
return obj->toString();
else
return "toString not defined";
}所以,如果class T定义了toString(),那么它就会使用它;否则,它就不会使用它。
发布于 2008-11-02 21:15:01
可以,使用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和gcc 4.1/4.3测试了它。我不知道它是否可以移植到其他运行不同编译器的平台上。
发布于 2012-02-06 08:27:05
这个问题很老了,但是使用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);
}现在给出一些解释。首先,我使用expression SFINAE从重载解析中排除serialize(_imp)函数,如果decltype中的第一个表达式无效(也就是该函数不存在)。
void()用于将所有这些函数的返回类型设置为void。
如果两个参数都可用,则0参数用于首选os << obj重载(文字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)){};Live example.
接下来是解释。首先,sfinae_true是一种帮助器类型,它基本上等同于编写decltype(void(std::declval<T>().stream(a0)), std::true_type{})。优点很简单,那就是它更短。
接下来,struct has_stream : decltype(...)最终继承自std::true_type或std::false_type,这取决于decltype在test_stream中的检查是否失败。
最后,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的上下文。
发布于 2008-11-05 01:08:39
C++允许使用SFINAE来实现这一点(请注意,对于C++11特性,这更简单,因为它支持几乎任意表达式上的扩展SFINAE -以下代码是为使用常见的C++03编译器而精心设计的):
#define HAS_MEM_FUNC(func, name) \
template<typename T, typename Sign> \
struct name { \
typedef char yes[1]; \
typedef char no [2]; \
template <typename U, U> struct type_check; \
template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
template <typename > static no &chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}上面的模板和宏试着实例化一个模板,给它一个成员函数指针类型和实际的成员函数指针。如果类型不适合,SFINAE会导致模板被忽略。用法如下:
HAS_MEM_FUNC(toString, has_to_string);
template<typename T> void
doSomething() {
if(has_to_string<T, std::string(T::*)()>::value) {
...
} else {
...
}
}但请注意,您不能只在if分支中调用该toString函数。由于编译器将在两个分支中检查有效性,因此在函数不存在的情况下将失败。一种方法是再次使用SFINAE (enable_if也可以从boost获得):
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };
HAS_MEM_FUNC(toString, has_to_string);
template<typename T>
typename enable_if<has_to_string<T,
std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
/* something when T has toString ... */
return t->toString();
}
template<typename T>
typename enable_if<!has_to_string<T,
std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
/* something when T doesnt have toString ... */
return "T::toString() does not exist.";
}祝你玩得开心。它的优点是,它也适用于重载的成员函数,也适用于const成员函数(记得使用std::string(T::*)() const作为成员函数指针类型!)。
https://stackoverflow.com/questions/257288
复制相似问题