С++ специализация функции шаблона внутри класса шаблона
Что такое синтаксис С++ для специализации функции шаблона внутри класса шаблона? Например, учтите, что у меня есть следующие два класса и их использование. Я хотел бы иметь возможность предоставлять специализированные реализации метода 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;
}
Я пытался угадать правильный синтаксис специализации в течение по крайней мере часа и не могу понять, что будет компилироваться. Любая помощь будет принята с благодарностью!
Ответы
Ответ 1
Итак, я использую другой подход к ответу на ваш вопрос. Я собираюсь начать с того, что делает то, что вы хотите, и работает. И тогда, может быть, мы сможем выяснить, как переставить его в нечто более близкое к тому, что вы действительно хотите:
#include <string>
#include <iostream>
int getIntThing(const ::std::string ¶m);
template <typename returnT>
returnT getThingFree(const ::std::string ¶m);
template <>
int getThingFree<int>(const ::std::string ¶m)
{
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 ¶m)
{
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 ¶m,
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 ¶m) {
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 ¶m) {
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
в пространстве имен полу-частных приложений.
Ответ 2
AFAIK (и эксперты по стандартам могут исправить меня), вы не можете специализировать шаблонную функцию шаблона класса без специализации самого класса...
то есть. следующее, я думаю, будет работать:
template <> template <> int X<Y>::getAThing<int>(std::string param) {
return getIntThing(param); // Some function that crunches on param and returns an int.
}
Ответ 3
С++ не имеет понятия частичной специализации для шаблонов функций. Тем не менее, вы можете получить тот же эффект, что и полная специализация, с помощью перегрузки функций.
Я предполагаю, что у вас есть что-то вроде этого, что на самом деле является одним из единственных способов сделать это.
template<class TYPE>
class MyInterface {
public:
template<class RETURN>
RETURN myFunction(RETURN& ref, ....);
};
В этом случае вы специализируетесь на "myFunction()", объявляя обычные функции-члены с нужным типом. Правила перегрузки функций С++ должны давать вам то, что вы хотите, например.
template<class TYPE>
class MyInterface {
public:
template<class RETURN>
RETURN myFunction(RETURN& ref, ....);
// String specialization
std::string myFunction(std::string& ref, ...);
};
В случае необходимости компилятор будет использовать функцию "std::string" и никогда не сможет использовать внутренний шаблон вообще.
Ответ 4
Для любопытных это, вероятно, решение, которое я собираюсь использовать в своем собственном коде. Это небольшое отклонение от ответа Omnifarious, что устраняет необходимость в дополнительном классе. Я все еще даю реквизит Omnifarious, так как большую часть работы он выполнял:
#include <iostream>
#include <string>
using namespace std;
// IMPORTANT NOTE: AdaptingFunctor has no access to the guts of class X!
// Thus, this solution is somewhat limited.
template<typename t1> class AdaptingFunctor {
public:
t1 operator() (string param);
};
// Can specialize AdaptingFunctor for each type required:
template<> int AdaptingFunctor<int>::operator() (string param)
{
return param.size(); // <=== Insert required type-specific logic here
}
// Additional specializations for each return type can go
// here, without requiring specialization of class c1 for X...
template <class c1> class X {
public:
template<typename returnT> returnT getAThing(std::string param)
{
AdaptingFunctor<returnT> adapter;
return adapter(param);
}
static std::string getName();
private:
c1 theData;
};
// Template definition of class method works ok...
template <class c1> std::string X<c1>::getName() {
return c1::getName();
}
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;
}
Ответ 5
Try
template <>
template <class T>
int X<T>::template getAThing<int>(std::string param)
{
return getIntThing(param);
}
Это все еще не компилируется, но оно ближе, чем вы.
Я думаю, что вы не можете специализировать членов в массе. Вы должны указать специальную специальную специализацию шаблона класса, прежде чем вы сможете начать специализировать свои члены.
Ответ 6
Вот простейший, самый простой способ, который я видел до сих пор:
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
}