1

Я знаю, что для функций и методов класса не поддерживается частная специализированная специализация, поэтому мой вопрос: Каковы общие решения или шаблоны для решения этой проблемы? Ниже Derived происходит от Base, и оба эти класса имеют виртуальные методы greet() и speak(). Foo имеет номер std::array<unique_ptr<T>, N> и используется в do_something(). Foo имеет два параметра шаблона: T (тип класса) и N (количество элементов std::array) Если N = 2, существует высоко оптимизированная версия do_something(). Предположим теперь, что параметр Foo10 не всегда является базовым классом Base. В идеале, я хотел бы написать следующий код, но это незаконно:Частичная функция/метод шаблона обходные методы

//ILLEGAL 
template<typename T> 
void Foo<T,2>::do_something() 
{ 
    arr_[0]->greet(); 
} 

Ниже приведен полный код и мой текущий (некрасиво) решение. Я должен специализироваться на do_something() дважды, один раз за Base и один раз за Derived. Это становится уродливым, если существует несколько методов, таких как do_something(), которые можно оптимизировать по специальному случаю N = 2, и если существует много подклассов Base.

#include <iostream> 
#include <memory> 

class Base 
{ 
public: 
    virtual void speak() 
    { 
    std::cout << "base is speaking" << std::endl; 
    } 
    virtual void greet() 
    { 
    std::cout << "base is greeting" << std::endl; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    void speak() 
    { 
    std::cout << "derived is speaking" << std::endl; 
    } 
    void greet() 
    { 
    std::cout << "derived is greeting" << std::endl; 
    } 
}; 

template<typename T, int N> 
class Foo 
{ 
public: 
    Foo(std::array<std::unique_ptr<T>, N>&& arr) : 
    arr_(std::move(arr)) 
    { 
    } 

    void do_something(); 

    std::array<std::unique_ptr<T>, N> arr_; 
}; 

template<typename T, int N> 
void Foo<T,N>::do_something() 
{ 
    arr_[0]->speak(); 
} 

//Want to avoid "copy-and_paste" of do_something() below 
template<> 
void Foo<Base,2>::do_something() 
{ 
    arr_[0]->greet(); 
} 

template<> 
void Foo<Derived,2>::do_something() 
{ 
    arr_[0]->greet(); 
} 

int main() 
{ 
    constexpr int N = 2; 
    std::array<std::unique_ptr<Derived>, N> arr = 
    { 
     std::unique_ptr<Derived>(new Derived), 
     std::unique_ptr<Derived>(new Derived) 
    }; 
    Foo<Derived, N> foo(std::move(arr)); 
    foo.do_something(); 
    return 0; 
} 

ответ

0

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

Первый направить запрос на статическую функцию в классе шаблона, который позволяет для частичных специализации:

template <int N> 
struct Helper { 
    template <typename T> 
    static void talk(T& t) { // Should be T const &, but that requires const members 
     t.speak(); 
    } 
}; 
template <> 
struct Helper<2> { 
    template <typename T> 
    static void talk(T& t) { 
     t.greet(); 
    } 
} 

;

Тогда реализация do_something будет:

template <typename T, int N> 
void Foo<T,N>::do_something() { 
    Helper<N>::talk(*arr_[0]); 
} 

В качестве альтернативы, вы можете использовать тег отправку, чтобы выбрать один из нескольких перегрузок:

template <int N> struct tag {}; 

template <typename T, int N> 
template <int M> 
void Foo<T,N>::do_something_impl(tag<M>) { 
    arr_[0]->speak(); 
} 

template <typename T, int N> 
void Foo<T,N>::do_something_impl(tag<2>) { 
    arr_[0]->greet(); 
} 

template <typename T, int N> 
void Foo<T,N>::do_something() { 
    do_something_impl(tag<N>()); 
} 

Где я создал тег тип, который может быть специализированным для любого возможного N. Вы также можете использовать существующие инструменты в C++ 11.

И, наконец, если вам нужно сделать что-то подобное для разных функций, вы можете использовать наследование и нажимать некоторые функции на базу, которая устраняет различия. Это можно сделать, либо нажимая общий код на базу, различия на промежуточный уровень и используя нижний уровень фронт тип, который просто наследуется от остальных (база содержит общий код, производные типы специализируются). Или, альтернативно, с CRTP (base (s)) содержат отличия, генерируемый код производного типа и вытягивает конкретные реализации из баз.

+0

Мне интересно узнать больше. Не могли бы вы связать меня с тем, что вы подразумеваете под «использованием существующих инструментов на C++». Благодарю. –

+0

@anon: 'std :: integ_constant', например, может использоваться для отправки тегов и соответствует потребностям данной конкретной проблемы. –

3

Хитрость заключается в том, чтобы направить реализации на шаблонный класс хелперов, и частичными специализироваться, что класс и/или использование тегов диспетчеризация:

namespace { 
    template<typename T, int N, bool isBase = std::is_base_of<Base, T>::value> 
    struct helper { 
     // general case: 
     void operator() (std::array<std::unique_ptr<T>, N>& arr_) const 
     { 
      arr_[0]->speak(); 
     } 
    }; 

    template<typename T> 
    struct helper<T, 2, true> 
    { 
     void operator() (std::array<std::unique_ptr<T>, 2>& arr_) const 
     { 
      arr_[0]->greet(); 
     } 
    }; 

    // You may add other specialization if required. 

} 

template<typename T, int N> 
void Foo<T,N>::do_something() 
{ 
    helper<T, N>()(arr_); 
} 
+0

Как бы вы использовали диспетчеризацию тегов для этого? Просто контур достаточно хорош. – kec

+1

@kec: Используйте 'std :: is_base_of :: type' как дополнительный параметр (поэтому перегрузка для 'std :: true_type' и' std :: false_type') и удалить параметр шаблона 'isBase'. – Jarod42

+0

@kec: [полный код] (http:/ideone.com/llvdN1) – Jarod42