为了账号安全,请及时绑定邮箱和手机立即绑定

是否可以编写模板来检查函数的存在?

是否可以编写模板来检查函数的存在?

天涯尽头无女友 2019-05-24 15:12:24
是否可以编写模板来检查函数的存在?是否可以编写一个模板来改变行为,具体取决于是否在类上定义了某个成员函数?这是我想写的一个简单例子:template<class T>std::string optionalToString(T* obj){     if (FUNCTION_EXISTS(T->toString))         return obj->toString();     else         return "toString not defined";}所以,如果class T已经toString()确定的话,就使用它; 否则,它没有。我不知道怎么做的神奇部分是“FUNCTION_EXISTS”部分。
查看完整描述

5 回答

?
Smart猫小萌

TA贡献1911条经验 获得超7个赞

是的,使用SFINAE,您可以检查给定的类是否提供某种方法。这是工作代码:

#include <iostream>struct Hello{
    int helloworld() { return 0; }};struct Generic {};    // SFINAE testtemplate <typename T>class has_helloworld{
    typedef char one;
    typedef long two;

    template <typename C> static one test( typeof(&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测试过它。我不知道它是否可以移植到运行不同编译器的其他平台


查看完整回答
反对 回复 2019-05-24
?
UYOU

TA贡献1878条经验 获得超4个赞

这个问题很老,但是使用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);}

现在进行一些解释。首先,如果内部的第一个表达式无效(也就是说,函数不存在),我使用表达式SFINAEserialize(_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)){};

实例。

并解释。首先,sfinae_true是一个帮助器类型,它基本上与写入相同decltype(void(std::declval<T>().stream(a0)), std::true_type{})。优点是它更短。
接下来,取决于签入是否失败,struct has_stream : decltype(...)从任一端std::true_typestd::false_type最后继承。 最后,为您提供所传递的任何类型的“值”,而无需您知道如何构建它。请注意,这只能在未评估的上下文中使用,例如,和其他。decltypetest_stream
std::declvaldecltypesizeof


请注意,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;}

由于同样的原因,intlong参数仍然存在。数组指针用于提供sizeof可以使用的上下文。


查看完整回答
反对 回复 2019-05-24
?
守着星空守着你

TA贡献1799条经验 获得超8个赞

虽然这个问题已经有两年了,但我还是敢补充一下。希望它能澄清以前无可争议的优秀解决方案。我采用了Nicola Bonelli和Johannes Schaub的非常有用的答案,并将它们合并为一个解决方案,即恕我直言,更易读,更清晰,不需要typeof扩展:

template <class Type>class TypeHasToString{
    // This type won't compile if the second template parameter isn't of type T,
    // so I can put a function pointer type in the first parameter and the function
    // itself in the second thus checking that the function has a specific signature.
    template <typename T, T> struct TypeCheck;

    typedef char Yes;
    typedef long No;

    // A helper struct to hold the declaration of the function pointer.
    // Change it if the function signature changes.
    template <typename T> struct ToString
    {
        typedef void (T::*fptr)();
    };

    template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
    template <typename T> static No  HasToString(...);public:
    static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));};

我用gcc 4.1.2检查了它。这个功劳主要归功于Nicola Bonelli和Johannes Schaub,如果我的回答可以帮助你,请给他们一个投票:)


查看完整回答
反对 回复 2019-05-24
  • 5 回答
  • 0 关注
  • 654 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信