2015-10-12 3 views
1

Я пытаюсь понять, как std :: enable_if работает с параметрами шаблона inn.enable_if customization custom traits

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

Их я пытался упростил код, обеспечивая собственные черты, чтобы проверить, если объект является экземпляром интерфейса или значения.

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

Но вторая версия не может скомпилировать со следующей ошибкой:

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> 

Может кто-то объяснить, что проблема со второй версией?

ответ

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

Это работает, потому что std::is_base_of предоставляет operator bool неявно преобразовать экземпляр этого в bool. Ваши черты не предусматривают этого, поэтому использование is_interface<T>{} в качестве bool недействительно.

Вы могли бы написать operator bool для ваших черт, но легко исправить, чтобы просто использовать ::value вместо:

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

Лично я думаю, что метод мечения-диспетчерская будет чище и легче поддерживать в вашем дело. Вот возможная реализация:

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

Если вам нужны какие-либо разъяснения по этому методу, просто спросите.