简体   繁体   English

enable_if模板特化自定义特征

[英]enable_if template specialization custom traits

I trying to understand how std::enable_if works inn template parameters. 我试图了解std :: enable_if如何使用inn模板参数。

#include <type_traits>
#include <iostream>
#include <memory>

using namespace std;


class Interface {};
class Value {};


class Stream
{
public:

    template<typename T,
            typename enable_if<
                is_base_of< Interface, T>{}
            >::type* = nullptr>
    void
    write(const shared_ptr<T>& v)
    {
        cerr << "Writing interface class" << endl;
    }

    template<typename T,
            typename enable_if<
                is_base_of< Value, T>{}
            >::type* = nullptr>
    void
    write(const shared_ptr<T>& v)
    {
        cerr << "Writing value class" << endl;
    }
};

class UserI : public Interface{};
class User : public Value{};

int main(int, char**)
{

    auto interface = make_shared<UserI>();
    auto value = make_shared<User>();

    Stream s;
    s.write(interface);
    s.write(value);

    return 0;
}

Them I tried to simplified the code by providing a custom traits to check if a object is an interface or value instance. 我试图通过提供自定义特征来检查对象是接口还是值实例来简化代码。

#include <type_traits>
#include <iostream>
#include <memory>

using namespace std;


class Interface {};
class Value {};

template<typename T>
struct is_interface
{
    const static bool value = is_base_of< Interface, T>::value;
};

template<typename T>
struct is_value
{
    const static bool value = is_base_of< Value, T>::value;
};

class Stream
{
public:

    template<typename T,
            typename enable_if<
                is_interface<T>{}
            >::type* = nullptr>
    void
    write(const shared_ptr<T>& v)
    {
        cerr << "Writing interface class" << endl;
    }

    template<typename T,
            typename enable_if<
                is_value<T>{}
            >::type* = nullptr>
    void
    write(const shared_ptr<T>& v)
    {
        cerr << "Writing value class" << endl;
    }
};

class UserI : public Interface{};
class User : public Value{};

int main(int, char**)
{

    auto interface = make_shared<UserI>();
    auto value = make_shared<User>();

    Stream s;
    s.write(interface);
    s.write(value);

    return 0;
}

But the second version fails to compile with the following error: 但第二个版本无法编译,出现以下错误:

test_t2.cc: In function ‘int main(int, char**)’:
test_t2.cc:58:26: error: no matching function for call to ‘Stream::write(std::shared_ptr<UserI>&)’
        s.write(interface);
                        ^
test_t2.cc:58:26: note: candidates are:
test_t2.cc:32:9: note: template<class T, typename std::enable_if<is_interface<T>{}>::type* <anonymous> > void Stream::write(const std::shared_ptr<_Tp1>&)
        write(const shared_ptr<T>& v)
        ^
test_t2.cc:32:9: note:   template argument deduction/substitution failed:
test_t2.cc:30:28: error: could not convert template argument ‘is_interface<UserI>{}’ to ‘bool’
                >::type* = nullptr>

Can someone explain what is the issue with the second version? 有人可以解释第二个版本的问题是什么?

typename enable_if<
    is_base_of< Interface, T>{}
>::type* = nullptr

That works because std::is_base_of provides operator bool to implicitly convert an instance of it to bool . 这是有效的,因为std::is_base_of提供了operator bool来隐式地将它的实例转换为bool Your traits do not provide that, so using is_interface<T>{} as a bool is not valid. 你的特征没有提供,所以使用is_interface<T>{}作为bool是无效的。

You could write operator bool for your traits, but the easy fix is to just use ::value instead: 您可以为您的特征编写operator bool ,但简单的解决方法是使用::value代替:

template<typename T,
        typename enable_if<
            is_interface<T>::value //here
        >::type* = nullptr>
void
write(const shared_ptr<T>& v)

Personally, I think a tag-dispatch method would be cleaner and more maintainable in your case. 就个人而言,我认为标签调度方法在您的情况下会更清晰,更易于维护。 Here's a possible implementation: 这是一个可能的实现:

class Stream
{
private:
    struct interface_tag{};
    struct value_tag{};
    //if you ever need more tags, add them here
    struct invalid_tag{};

    template <typename T>
    struct get_tag {
        static interface_tag tagger (Interface*);
        static value_tag tagger (Value*);
        //add any mappings for other tags
        static invalid_tag tagger (...);

        using tag = decltype(tagger(std::declval<T*>()));
    };

    //convenience alias
    template <typename T> using tag_t = typename get_tag<T>::tag;

public:
    //clean public interface
    template <typename T>
    void write (const shared_ptr<T>& v) {
        write(v, tag_t<T>{});   
    }

private:
    //no more horrible std::enable_if
    template<typename T>
    void
    write(const shared_ptr<T>& v, interface_tag)
    {
        cerr << "Writing interface class" << endl;
    }

    template<typename T>
    void
    write(const shared_ptr<T>& v, value_tag)
    {
        cerr << "Writing value class" << endl;
    }
};

Live Demo 现场演示

If you need any clarification on this method, just ask. 如果您需要对此方法有任何澄清,请询问。

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

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