c ++ SFINAE检查指向类的方法

时间:2016-02-28 13:14:59

标签: c++ sfinae

我有这样的代码:

template<class TABLELOADER = void>
class DiskFileFlush{
   TABLELOADER *_loader;

public:
   void process(){
       // I want to call this,
       // only if loader->refresh() exists.
       notifyLoader();
   }

    bool notifyLoader(){
        if (loader != nullptr)
            return loader->refresh();

        return false;
    }
};

我想仅在notifyLoader()存在的情况下致电loader->refresh()

我也使用void作为默认类型,有没有更好的方法来做同样的事情?

4 个答案:

答案 0 :(得分:2)

诀窍是一起使用SFINAE和模板专业化。

以下示例使用gcc 5.3编译:

#include <iostream>

template<typename T> struct has_refresh_operator
{
        //! Yes return value

        typedef char yes[1];

        //! No return value

        typedef char no[2];

        template<typename S>
        static yes &check(decltype( (*(S *)nullptr)->refresh()) *);

        template<typename S>
        static no &check(...);

        //! Determine whether the class implements ->refresh() method.

    static const bool value = sizeof(check<T>(0)) == sizeof(yes);
};

// Now, use a specialized template to figure out what to do:

template<bool> class invoke_refresh;

template<> class invoke_refresh<true> {
public:

    template<typename T>
    static inline auto doit(const T &t)
    {
        return t->refresh();
    }
};

template<> class invoke_refresh<false> {
public:
    template<typename T>
    static inline bool doit(const T &t)
    {
        return false;
    }
};

// Now, let's try some examples:

template<class TABLELOADER = void>
class DiskFileFlush{
   TABLELOADER *_loader;

public:

    DiskFileFlush(TABLELOADER *p) : _loader(p) {}

   void process(){
       // I want to call this,
       // only if loader->refresh() exists.
       notifyLoader();
   }

    bool notifyLoader(){
        if (_loader != nullptr)
            return invoke_refresh<has_refresh_operator<TABLELOADER *>::value>::doit(_loader);
        return false;
    }
};

// Try: class implements refresh(), class doesn't implement refresh(),
// and a void pointer.

class foo1 {

public:
    bool refresh()
    {
        std::cout << "Foo" << std::endl;
        return true;
    }
};

class foo2 {

public:
};

int main()
{
    foo1 bar1;
    foo2 bar2;

    DiskFileFlush<foo1> foobar1(&bar1);
    DiskFileFlush<foo2> foobar2(&bar2);
    DiskFileFlush<void> baz(0);

    foobar1.process();
    foobar2.process();
    baz.process();
}

答案 1 :(得分:2)

SFINAE现在可以以更加严谨的方式完成,这要归功于表达式 - SFINAE:

template<class TABLELOADER = void>
class DiskFileFlush{
   TABLELOADER *_loader;

public:
   void process(){
       notifyLoader(0);
   }

    // Best match for the call above (0 is an int)
    template <class TL = TABLELOADER>
    auto notifyLoader(int) -> decltype(std::declval<TL&>().refresh()) {
        std::cout << "Refreshing!\n";

        if (_loader != nullptr)
            return _loader->refresh();

        return false;
    }

    // Fallback with low-priority varargs
    bool notifyLoader(...) {
        std::cout << "Not refreshing!\n";
        return false;
    }
};

Live on Coliru

您可能希望通过提供调用notifyLoader()的公开notifyLoader(0)方法将其隐藏在您班级的公共界面中。

答案 2 :(得分:0)

我接受了答案,但我想评论我是如何实际做到的。

由于我不需要这个类TABLELOADER,但是调用这个方法,我做了明确的模板专业化。

我同意它不完全是SFINAE并且它不会编译,如果我使用的类型没有loader->refresh()且与std::nullptr_t不同。

最后看起来如何:
(代码简化,可能有错误)

template<class TABLELOADER = std::nullptr_t>
class DiskFileFlush{
   TABLELOADER *_loader;

public:
   void process(){
       // I want to call this,
       // only if loader->refresh() exists.
       notifyLoader(_loader);
   }

   template<class T>
   static bool _notifyLoader(T *loader){
       if (loader)
        return loader->refresh();

       return false;
   }

   static bool _notifyLoader(std::nullptr_t *){
       return false;
   }
};

答案 3 :(得分:0)

这是尽可能接近难以捉摸的$scope.dtOptions = DTOptionsBuilder.newOptions() .withPaginationType('full_numbers') .withDOM('frtip') .withButtons([ 'copy', 'print', 'excel' ]); 的最直接方式。

static_if(can_do_this)