2016-12-28 2 views
1

Это мой упрощенный код:Templating метод двойного шаблонный класс

template<typename device, template<typename device> class protocol> 
class MyClass 
{ 
public: 
    template<typename select> 
    bool method() 
    { 
    // Code 
    } 
}; 

Я хочу method действовать разные точки зрения типа.

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

ответ

1

К примеру, с помощью SFINAE (если вы принимаете решение на C++ 11)

#include <iostream> 
#include <type_traits> 

template <typename> 
struct protocol_1 
{ }; 

template <typename> 
struct protocol_2 
{ }; 

template<typename device, template<typename> class protocol> 
class MyClass 
{ 
    public: 
     template<typename select, typename p = protocol<device>> 
     typename std::enable_if< 
     std::is_same<p, protocol_1<device>>::value, bool>::type 
     method() 
     { return true; } 

     template<typename select, typename p = protocol<device>> 
     typename std::enable_if< 
     std::is_same<p, protocol_2<device>>::value, bool>::type 
     method() 
     { return false; } 
}; 

int main() 
{ 
    MyClass<int, protocol_1> m1; 
    MyClass<int, protocol_2> m2; 

    std::cout << m1.method<int>() << std::endl; // print 1 (true) 
    std::cout << m2.method<void>() << std::endl; // print 0 (false) 
} 

--- EDIT ---

Как указано на Yakk (спасибо!), Это решение является слабым, потому что используется значение по умолчанию шаблона, которое может быть раскрыто и распространено.

Пример; с

MyClass<int, protocol_1>{}.method<void>(); 

называется "protocol_1" версия method(), используя значение по умолчанию для p; но explciting p следующим

MyClass<int, protocol_1>{}.method<void, protocol_2<int>>(); 

называется «protocol_2» версию method() над istance из в MyClass на основе protocol_1

Чтобы избежать этой проблемы можно добавить static_assert(), как в версии method() , чтобы проверить и налагают, что p равно значению по умолчанию (protocol<device>)

Я имею в виду ... как последовать

template<typename select, typename p = protocol<device>> 
    typename std::enable_if< 
     std::is_same<p, protocol_1<device>>::value, bool>::type 
    method() 
    { 
    static_assert(std::is_same<p, protocol<device>>::value, "!"); 

    return true; 
    } 

    template<typename select, typename p = protocol<device>> 
    typename std::enable_if< 
    std::is_same<p, protocol_2<device>>::value, bool>::type 
    method() 
    { 
    static_assert(std::is_same<p, protocol<device>>::value, "!"); 

    return false; 
    } 

Так

MyClass<int, protocol_1>{}.method<void, protocol_2<int>>(); 

генерировать ошибку компилятора.

+3

Обратите внимание, что этот подход очень хрупкий, поскольку, если тела действительны только в предположении, никто не проходит шаблонный аргумент 2, вы получаете плохо сформированную программу, для которой не требуется диагностика. Что еще хуже, компилятор не диагностирует проблему; ваш код будет работать отлично. – Yakk

+0

@skyp поиск «нет необходимости диагностики», в стандарте есть только дюжина или около того. Один о шаблонах должен иметь действительные специализации (что означает что-то другое в стандарте, чем разговорное) – Yakk

+0

@Yakk. Спасибо. – skypjack

1

Использование внешнего помощника другое возможное решение:

template <typename T> 
struct B { 
}; 
template <template<typename> class protocol, typename select> 
struct helper { 
    static bool do_real() { 
     return true; 
    } 
}; 
template <typename select> 
struct helper<B, select> { 
    static bool do_real() { 
     return false; 
    } 
}; 
template<typename device, template<typename> class protocol> 
class MyClass 
{ 
public: 
    template<typename select> 
    bool method() 
    { 
    return helper<protocol, select>::do_real(); 
    } 
}; 

Как this

0

Вы можете частичной специализации класса, что-то вроде:

template<typename device, template<typename> class protocol> 
class MyClass; 

template <typename> struct protocole1 { /* ... */ }; 
template <typename> struct protocole2 { /* ... */ }; 

template<typename device> 
class MyClass<device, protocol1> 
{ 
public: 
    template<typename select> 
    bool method() 
    { 
    // Code for protocole1 
    } 
}; 

template<typename device> 
class MyClass<device, protocol2> 
{ 
public: 
    template<typename select> 
    bool method() 
    { 
    // Code for protocole2 
    } 
}; 
2

Вы не упомянули, что целевой стандарт, поэтому я постараюсь дать вам альтернативу для всех сезонов.


В C++ 11/14 для этого можно использовать диспетчеризацию меток и функцию перегрузки.
вытекает минимальный, рабочий пример:

#include<iostream> 

template<typename> struct protocol_a {}; 
template<typename> struct protocol_b {}; 

template<typename device, template<typename> class protocol> 
class MyClass { 
    template<template<typename> class> struct tag {}; 

    template<typename select> 
    bool method(tag<protocol_a>) { 
     std::cout << "protocol_a" << std::endl; 
     return true; 
    } 

    template<typename select> 
    bool method(tag<protocol_b>) { 
     std::cout << "protocol_b" << std::endl; 
     return false; 
    } 

public: 
    template<typename select> 
    bool method() { 
     return method<device>(tag<protocol>{}); 
    } 
}; 

int main() { 
    MyClass<int, protocol_a> mca; 
    mca.method<void>(); 
    MyClass<int, protocol_b> mcb; 
    mcb.method<void>(); 
} 

Довольно компактный. Он не требует дополнительных классов, частичных специализаций или выражений sfinae.Недостаток (если мы можем назвать это недостатком) заключается в том, что у вас есть еще один уровень косвенности.


В C++ 17 вы можете использовать if constexpr, чтобы получить тот же результат.
вытекает минимальный, рабочий пример:

#include<type_traits> 
#include<iostream> 

template<typename> struct protocol_a {}; 
template<typename> struct protocol_b {}; 

template<typename device, template<typename> class protocol> 
class MyClass { 
public: 
    template<typename select> 
    bool method() { 
     if constexpr(std::is_same_v<protocol<device>, protocol_a<device>>) { 
      std::cout << "protocol_a" << std::endl; 
      return true; 
     } else if constexpr(std::is_same_v<protocol<device>, protocol_b<device>>) { 
      std::cout << "protocol_b" << std::endl; 
      return false; 
     } 
    } 
}; 

int main() { 
    MyClass<int, protocol_a> mca; 
    mca.method<void>(); 
    MyClass<int, protocol_b> mcb; 
    mcb.method<void>(); 
} 

Более компактный, но C++ 17 не может быть вариантом.
Посмотрите, как работает wandbox.

Смежные вопросы