2010-12-14 3 views
4

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

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. 

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

+0

Всякий раз, когда компилятор умирает, это проблема с компилятором - неспособность рассказать вам, как неправильно ваш код. В коде есть несколько ошибок, но это не объясняет сообщение «внутренняя ошибка». Конечно, правильная вещь для компилятора будет заключаться в том, чтобы не компилировать код в любом случае ... но он должен обеспечить лучшую диагностику, чем «Я забился до смерти» –

+0

@David: составители компиляторов не являются богами бесплатного программного обеспечения! Это может быть или не может быть «исправлено в более позднем выпуске». –

ответ

7

Обычно это выглядит следующим образом:

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); } 
+0

Вы забыли сделать его статическим. – Puppy

+0

@DeadMG: Хороший улов, спасибо –

+0

Спасибо, что сделал трюк. –

1

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

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); 
}; 
+0

Я никогда не видел друга, используемого в этом контексте. Что оно делает? – Ferruccio

+0

@Ferruccio: что бы вы ожидали от этого: друзья X для любых аргументов шаблона A и B. – Puppy

+0

'error C2255: 'friend': не разрешено за пределами определения класса' ... –

4

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

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

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 } 

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

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

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

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

+0

смарт-решение. +1 за хорошую работу! – Nawaz

+0

@Nawaz: Как я уже сказал, это просто повторное использование того, что уже использует STL у меня: p –

+0

вот что мне понравилось в этом решении. нет необходимости определять новые классы/структуры. :-) – Nawaz

1

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

Вы пропускаете 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) {} 

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

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

+0

Yep - код здесь не является причиной ошибки компилятора; Я пропустил несколько вещей, поскольку я упрощал код для вопроса. (Забыть определение «класс» глупо!) –