Частичная специализация оператора () - PullRequest
4 голосов
/ 14 декабря 2010

Один из моих классов объявляет шаблонную функцию:

template<class A, class B>
A do_something(const std::vector<B> &data)

, которую я бы хотел частично специализировать на typename A.B - это семейство типов, которые реализуют довольно минимальный интерфейс, и мы используем их много, поэтому я бы хотел, чтобы моя специализация была общей для B.Я подозреваю, что это вдвойне неприятно, поскольку typename A используется только в качестве возвращаемого типа.

Из Интернета я понял, что не могу частично специализировать функцию, поэтому я создал класс какследующее:

template<class A, class B> 
class do_something_implementation {
  public:
    do_something_implementation(const std::vector<B> &data_) {
      data = data_;
    }

  int do_something_implementation<int, B>::operator()() {
    /* Complicated algorithm goes here... */
  }

  double do_something_implementation<double, B>::operator()() {
    /* Different complicated algorithm goes here... */
  }

  private:
      std::vector<B> data;
}

Когда я пытаюсь скомпилировать это (используя Visual Studio 2008), компилятор падает (!), и я получаю следующую ошибку:

fatal error C1001: An internal error has occurred in the compiler.

Я предполагаю, что этоМоя проблема, а не проблема компилятора.Есть ли лучший способ выразить частичную специализацию, к которой я стремлюсь?

Ответы [ 4 ]

7 голосов
/ 14 декабря 2010

Обычно это выглядит так:

template <typename A, typename B>
struct DoSomethingHelper
{
    static A doIt(const std::vector<B> &data);
};

template <typename B>
struct DoSomethingHelper<double, B>
{
    static double doIt(const std::vector<B> &data) { ... }
};

template <typename B>
struct DoSomethingHelper<int, B>
{
    static int doIt(const std::vector<B> &data) { ... }
};

template<class A, class B>
A do_something(const std::vector<B> &data)
{ return DoSomethingHelper<A, B>::doIt(data); }
4 голосов
/ 14 декабря 2010

Теперь, когда вы увидели классический метод перенаправления на статический метод, существует фактически другой способ, когда тип, для которого нужно специализироваться, является «завершенным».

Возможно, вам не удастся частично специализировать функцию,но вы можете полностью его перегрузить.

template <typename A, typename B>
A do(std::vector<B> const& data) { return this->doImpl(data, (A*)0); }

template <typename A, typename B>
A doImpl(std::vector<B> const& B, A*) { // generic implementation }

template <typename B>
int doImpl(std::vector<B> const& B, int*) { // int implementation }

template <typename B>
double doImpl(std::vector<B> const& B, double*) { // double implementation }

Хитрость заключается в том, чтобы передать «1006» * «неиспользуемый» аргумент с единственной целью - действительно выбрать правильную реализацию (благодаря разрешению перегрузки).

Здесь я просто решил передать (A*)0, потому что для этого не требуется конструктор A (в случае, если он не тривиален).

Эта идиома диспетчеризации используется в STL для реализациинекоторый алгоритм с большей эффективностью для некоторых категорий итераторов (например, std::distance - это O (1) для случайных итераторов).

Я считаю его гораздо более легким, чем использование вспомогательного класса со статическими методами и частичными специализациями... но может это только я :)

1 голос
/ 14 декабря 2010

Не решение вашей проблемы (есть пара уже там), но некоторые вещи, которые ошибочны в вашем коде:

Вам не хватает ключевого слова struct или class вобъявление класса шаблона:

template <typename A, typename B> struct do_something_implementation {
//                                ^^^^^^

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

class A {
   void A::foo() {} // Error, should be: void foo() {}
};

Специализации шаблона членане может появляться внутри определения класса, но на уровне пространства имен:

class B {
   template <typename T> void foo( T );
};
template <> void B::foo<int>( int ) {}
template <> void B::foo<double>( double ) {}

Кроме того, в вашем случае функция-член - это не шаблон, а скорее функция-член без шаблонов (шаблон - это содержащий класс, а не сама функция).То, что ваш код фактически пытается сделать, - это определить функции-члены других классов внутри общего шаблона, что-то вроде попытки сделать.

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

1 голос
/ 14 декабря 2010

Люди обычно просто ждут статической реализации.

template<class A, class B> class X;
template<class A, class B> friend class X;
template<class A, class B> class X {
public:
    static A do_something(class_type* not_this, const std::vector<B>& data) {
        //...
    }
};
// partially specialize
template<class A, class B>
A do_something(const std::vector<B> &data) {
    return X<A, B>::do_something(this, data);
};
...