C ++ специализация шаблонной функции внутри шаблонного класса - PullRequest
29 голосов
/ 14 февраля 2011

Что такое синтаксис C ++ для специализации шаблонной функции внутри класса шаблона? Например, предположим, что у меня есть следующие два класса и их использование. Я хотел бы иметь возможность предоставить специализированные реализации метода X :: getAThing () для разных типов. Например: int, std :: string, произвольный указатель или класс и т. Д.

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This blows up with the error:
// error: prototype for 'int X<c1>::getAThing(std::string)' does not match any in class 'X<c1>'
template <class c1> template <typename returnT> int X<c1>::getAThing(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}

// More specialized definitions of getAThing() for other types/classes go here...

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << endl;
   cout << "An int thing: " << anIntThing << endl;
}

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

Ответы [ 6 ]

18 голосов
/ 14 февраля 2011

AFAIK (и эксперты по стандартам могут меня поправить), вы не можете специализировать шаблонную функцию шаблона класса, не специализируя сам класс ...

т.е. я думаю, что будет работать следующее:

template <> template <> int X<Y>::getAThing<int>(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}
7 голосов
/ 13 апреля 2012

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

Я предполагаю, что у вас есть что-то подобное, что на самом деле является единственным способом сделать это.

template<class TYPE>
class MyInterface {
public:
    template<class RETURN>
    RETURN myFunction(RETURN& ref, ....);
};

В этом случае вы специализируете myFunction (), объявляя обычные функции-члены с желаемым типом. Правила перегрузки функций C ++ должны дать вам то, что вы хотите, например,

template<class TYPE>
class MyInterface {
public:
    template<class RETURN>
    RETURN myFunction(RETURN& ref, ....);

    // String specialization
    std::string myFunction(std::string& ref, ...);
};

Компилятор будет использовать функцию "std :: string", где это уместно, и может вообще не использовать внутренний шаблон.

6 голосов
/ 14 февраля 2011

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

#include <string>
#include <iostream>

int getIntThing(const ::std::string &param);

template <typename returnT>
returnT getThingFree(const ::std::string &param);

template <>
int getThingFree<int>(const ::std::string &param)
{
   return getIntThing(param);
}

// More specialized definitions of getAThing() for other types/classes
// go here...

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This also works, but it would be nice if I could explicitly specialize
// this instead of having to explicitly specialize getThingFree.
template <class c1>
template <class RT>
RT X<c1>::getAThing(std::string param) {
   // Some function that crunches on param and returns an RT.
   // Gosh, wouldn't it be nice if I didn't have to redirect through
   // this free function?
   return getThingFree<RT>(param);
}

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   using ::std::cout;
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << '\n';
   cout << "An int thing: " << anIntThing << '\n';
}

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

#include <string>
#include <iostream>

template <class c1> class X;

int getIntThing(const ::std::string &param)
{
   return param.size();
}

// You can partially specialize this, but only for the class, or the
// class and return type. You cannot partially specialize this for
// just the return type. OTOH, specializations will be able to access
// private or protected members of X<c1> as this class is declared a
// friend.
template <class c1>
class friendlyGetThing {
 public:
   template <typename return_t>
   static return_t getThing(X<c1> &xthis, const ::std::string &param,
                            return_t *);
};

// This can be partially specialized on either class, return type, or
// both, but it cannot be declared a friend, so will have no access to
// private or protected members of X<c1>.
template <class c1, typename return_t>
class getThingFunctor {
 public:
   typedef return_t r_t;

   return_t operator()(X<c1> &xthis, const ::std::string &param) {
      return_t *fred = 0;
      return friendlyGetThing<c1>::getThing(xthis, param, fred);
   }
};

template <class c1> class X {
public:
   friend class friendlyGetThing<c1>;

   template<typename returnT> returnT getAThing(std::string param) {
      return getThingFunctor<c1, returnT>()(*this, param);
   }
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

class Y {
public:
   static std::string getName() { return "Y"; }
};

template <class c1>
class getThingFunctor<c1, int> {
 public:
   int operator()(X<c1> &xthis, const ::std::string &param) {
      return getIntThing(param);
   }
};

// More specialized definitions of getAThingFunctor for other types/classes
// go here...

int main(int argc, char* argv[])
{
   using ::std::cout;
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << '\n';
   cout << "An int thing: " << anIntThing << '\n';
}

Я бы порекомендовал объявить getThingFunctor и friendlyGetThing в полуприватном пространстве имен утилит.

1 голос
/ 14 февраля 2011

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

0 голосов
/ 04 мая 2011

Вот самый простой и легкий способ, который я когда-либо видел, чтобы сделать это:

template <class T1>
struct MyClass {
  template <class T2>
  void MyFunction();
};

template <class T1>
template <class T2>
void MyClass<T1>::MyFunction() {  // NOTE:  NO T2 on this line.
  // Code goes here
}
0 голосов
/ 14 февраля 2011

Попробуйте

template <>
template <class T>
int X<T>::template getAThing<int>(std::string param)
{
   return getIntThing(param);
}

Это все еще не компилируется, но ближе, чем вы.

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

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