简体   繁体   English

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

[英]c++ SFINAE check for method in pointer to class

i have code like this: 我有这样的代码:

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;
    }
};

I want to call notifyLoader() only if loader->refresh() exists. 我想只在loader->refresh()存在时才调用notifyLoader()

I also use void as default type, is there some better way to do the same? 我也使用void作为默认类型,有没有更好的方法来做同样的事情?

The trick is to use SFINAE and template specialization, together. 诀窍是一起使用SFINAE和模板专业化。

The following example compiles with gcc 5.3: 以下示例使用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();
}

SFINAE can now be done in a much terser way, thanks to expression-SFINAE : 由于表达式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 住在Coliru

You'll probably want to hide this from the public interface of your class, by providing a public notifyLoader() method that calls notifyLoader(0) . 您可能希望通过提供调用notifyLoader(0)的公共notifyLoader()方法将其隐藏在类的公共接口中。

I accepted the answer, but I want to comment how I actually did it. 我接受了答案,但我想评论我是如何实际做到的。

Since I do not needed this class TABLELOADER for anything, but calling this method, I made explicit template specialization. 因为我不需要这个类TABLELOADER ,但是调用这个方法,我做了明确的模板专业化。

I agree it is not exactly SFINAE and it wont compile, if I use type that does not have loader->refresh() and is different from std::nullptr_t . 我同意它不完全是SFINAE并且它不会编译,如果我使用的类型没有loader->refresh()并且与std::nullptr_t不同。

Here how it look like at the end: 这里最后看起来如何:
(code is simplified and probably have errors) (代码简化,可能有错误)

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;
   }
};

This is the most straightforward way to get as close to the elusive static_if(can_do_this) as possible. 这是尽可能接近难以捉摸的static_if(can_do_this)的最简单方法。

// the if branch
template <typename T> 
    auto do_refresh (T* loader) -> decltype(
                                       loader->refresh(), // <-- "can_do_this" part
                                       bool())
    {
        loader->refresh();
        return false;
    };

// the else branch
template <typename T>
    auto do_refresh (...) -> bool
    {
        return false;
    }

bool refresh()
{
    return do_refresh(_loader);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM