Специализация шаблона метода для классов в пространстве имен - PullRequest
5 голосов
/ 25 января 2012

Я использую следующий «трюк» времени компиляции (основанный на ADL), чтобы создать функцию, которая является допустимой / определенной / вызываемой только классами в одном и том же пространстве имен.

    namespace Family1
    {
        struct ModelA{};
        struct ModelB{};

        template<typename T>
        bool is_in_Family1(T const& t) 
        {
            return true;
        }
    };

    namespace Family2
    {
        struct ModelC{};

        template<typename T>
        bool is_in_Family2(T const& t) 
        {
            return true;
        }
    };


    Family1::ModelA mA;
    Family2::ModelC mC;

    is_in_Family1(mA);          // VALID
    is_in_Family1(mC);          // ERROR

Теперь я хотел бы использовать этот принцип (или что-то подобное), чтобы получить специализацию Foo::Bar (ниже) для классов, принадлежащих к каждому из пространств имен, например. Family1.

    // I would like to specialize the method template Bar for classes in Family1 
    // namespace; and another specialization for classes in Family2 namespace
    struct Foo
    {
        template<typename T>
        void Bar( T& _T ){}
    };

Для простоты обслуживания и большого количества классов в каждом пространстве имен, если это возможно, я бы хотел выполнить эту проверку, не называя все классы в пространстве имен.

Ответы [ 2 ]

1 голос
/ 27 января 2012

У вашего "трюка" есть одна большая проблема.Попробуйте позвонить is_in_Family1(make_pair(Family1::ModelA(), Family2::ModelC()), и вы увидите, что возвращаем true, потому что ADL будет искать оба пространства имен ModelA и ModelC (из-за pair<ModelA, ModelC>).

Игнорирование этой проблемы, сиспользуя ваши функции, это просто.

template<typename T> struct int_ { typedef int type; };

struct Foo
{
    template<typename T, 
             typename int_<decltype(is_in_Family1(*(T*)0))>::type = 0
    >
    void Bar( T& t ){}

    template<typename T, 
             typename int_<decltype(is_in_Family2(*(T*)0))>::type = 0
    >
    void Bar( T& t ){}
};

Это вызывает Bar в зависимости от того, находится ли он в family2 или family1.

struct Foo
{
    template<typename T, 
             typename int_<decltype(is_in_Family1(*(T*)0))>::type = 0
    >
    void Bar( T& t, long){}

    template<typename T,
             typename int_<decltype(is_in_Family2(*(T*)0))>::type = 0
    >
    void Bar( T& t, long){}

    template<typename T>
    void Bar( T& t, int) {}

    template<typename T>
    void Bar( T& t ) { return Bar(t, 0); }
};

У этого также есть общий запасной вариант.И ваш код имел неопределенное поведение, потому что вы использовали зарезервированное имя.Не используйте _T.

0 голосов
/ 26 января 2012

Самый быстрый способ, который я нашел, это использовать черты типа Boost 'is_base_of <>

Я пытался использовать наследование со специализацией шаблона, но это не сработало, потому что наследование игнорируется при использовании специализации шаблона, поэтому вам придется специализироваться для каждой модели. Ответ на Частичная специализация для родителя нескольких классов объясняет проблему.

Использование признаков типа работает, если вы сделаете Family1 :: ModelA и Family :: ModelB подклассами Family1: Family1Type и Family2 :: ModelC подклассом Family2 :: Family2Type:

#include <iostream>
#include <boost/type_traits/is_base_of.hpp>

namespace Family1{

    struct Family1Type{};

    struct ModelA :public Family1Type{};
    struct ModelB :public Family1Type{};

    template<typename T>
    bool is_in_Family1(const T& t){
        return boost::is_base_of<Family1::Family1Type,T>::value;
    }
};

namespace Family2{
    struct Family2Type{};

    struct ModelC :public Family2Type{};

    template<typename T>
    bool is_in_Family2(const T& t){
        return boost::is_base_of<Family2::Family2Type,T>::value;
    }

};

using namespace std;
int main(int argc, char *argv[]) {

    Family1::ModelA mA;
    Family2::ModelC mC;

    std::cout << "mA is in Family1?  " << is_in_Family1(mA) << std::endl;
    std::cout << "mC is in Family2?  " << is_in_Family2(mC) << std::endl;

    //std::cout << "mC is in Family1?  " << is_in_Family1(mC) << std::endl; //ERROR!
    //std::cout << "mA is in Family2?  " << is_in_Family2(mA) << std::endl; //ERROR!

    return 0;
}

Это приводит к следующему выводу:

mA is in Family1?  1
mC is in Family2?  1

Я не думаю, что есть способ объявить Foo и специализировать Foo::Bar<> в другом пространстве имен согласно Специализация 'template struct std :: less' в другом пространстве имен

...