C++ Specialization of Template Function Inside Template Class

C++ specialization of template function inside template class

So, I'm taking a different approach to answering your question. I'm going to start from something that sort of does what you want, and works. And then maybe we can figure out how to permute it into something closer to what you really want:

#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';
}

Here is another idea that sort of works, and isn't exactly what you want, but is closer. I think you've thought of it yourself. It's also rather ugly in the way it uses type deduction.

#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';
}

I would recommend declaring getThingFunctor and friendlyGetThing in a semi-private utility namespace.

Templated function template specialization

A partial specialization is when you specify part of the type of the template, but not the whole thing. For example,

template <>
template<class T> set<T> convertTo<set<T>>(const char* str)

would partially specialize for set<T> if partial function specialization were allowed.

The two main ways of handling this are to instead overload, by removing the template<> part, or better yet switch to using template class specialization. The problem with overloading is that it looks somewhat different if you are overloading with another template (as with set<T>) or with a single type (as with int), and mixing specialization and overload almost certainly doesn't work as you expect.

Therefore, template class specialization is usually the best way to go, and can be done like this:

// Generic version
template <typename T>
class Converter {
public:
T operator() (const char* str) {
T output;
stringstream ss(str, stringstream::in);
ss >> output;
return output;
}
};

template <>
class Converter<int> {
public:
int operator() (const char* str) {
return atoi(str);
}
};

// ...

template <typename T>
T convertTo(const char* str) {
return Converter<T>{}(str);
}

That way you can use any type of specialization you want for the class without issue.

C++ - specialize function template on a templated class with a non type template parameter

Partial template specializations are not allowed for function templates.

Use SFINAE or class templates

template <class T>
struct validateType : std::false_type {};

template <class A, A val, class B>
struct validateType<Foo<A, val, B>> : std::true_type {};

Edit:

Is this supposed to work for template functions as well?

NO. Partial template specializations are not allowed for function templates.

for template function, use SFINAE.

For example, this sample check weather T is unsigned integer type(C++17).

template<typename T, std::enable_if_t<std::is_unsigned_v<T>, std::nullptr_t> = nullptr>
T foo(T n);
  • std::is_unsigned_v was added in C++17. before C++17, use std::is_unsigned<T>::value.

    https://en.cppreference.com/w/cpp/types/is_unsigned
  • std::enable_if_t was added in C++14. before C++14, use typename std::enable_if<con, T>::type.

    https://en.cppreference.com/w/cpp/types/enable_if
  • std::nullptr_t can hole only one value(nullptr) so that I use it for SFINAE enabler.

    (ja) https://qiita.com/kazatsuyu/items/203584ef4cb8b9e52462

However, in your case, you chould use class templates. It's simplest way to use class templates to check wether T is template class foo(BTW, not for template class foo, std::is_same is simplest way).

Template function specialization for template class

As Columbo mentioned in his comment, you should apply the standard workaround for lack of partial specialization support for functions: delegation to a partially specialized class:

template <typename T>
struct GetImpl;

template <typename T>
T Get() { return GetImpl<T>::Do(); }

and now use partial specialization on struct GetImpl<T> { static T Do(); } instead of Get<T>()

Template member function specialization in a template class

Why does this member function specialization get error?

When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.



Are there some better solutions?

Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.

#include <type_traits> // std::false_type, std::true_type
#include <vector>

// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};

template<typename T>
class A /* final */
{
T mData;

public:
// ...constructor

void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};

(See Live Online)

This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.


If you don not have access to C++17, other option is to SFINAE the members(Since c++11).

#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>

// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};

template<typename T>
class A /* final */
{
T mData;

public:
// ...constructor

template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}

template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};

(See Live Online)



What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?

You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post

#include <type_traits> // std::false_type, std::true_type
#include <vector>

// custom MyVector (An example)
template<typename T> struct MyVector {};

template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};

template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};

And the print function could be in c++17:

void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}

(See Live Online)

Class template specialization within template class

You can't specialize templates in non-namespace scope, like a struct in your case.

You would have to put the specializations outside of the struct definition:

template<typename TYPE> template<>
struct MyStruct<TYPE>::Nested<...> {};

But now you have another problem, if you want to specialize a template inside of a template class, you'll have to specialize it for every template class. You can't just specialize one member function, you have to specialize the whole class.

So, you need to do this:

template<> template<>
struct MyStruct<int>::Nested<...> {};

Also, you really don't need SFINAE for this:

template<typename SELECTOR>
struct Nested; // Default invalid SELECTOR

template<>
struct Nested<YES> { /*...*/ };

template<>
struct Nested<NO> { /*...*/ };

Template method of template class specialization

Unfortunately, you can't specialise a template that's a member of a class template, without specialising the outer template:

C++11 14.7.3/16: In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well.

I think your best option is to add the extra parameter to MyClass, and then partially specialise that.

template class method specialization using concepts

There are four syntactical methods of applying constraints to a function.

  1. Type constraint in a template parameter list; template< Concept TypeID >.
  2. Requires clause after a template parameter list; template< class TypeID > requires constexpr-andor-requires-expression.
  3. Constraint on auto in an abbriviated function template; void f(Concept auto id);.
  4. Requires clause after a function declaration; template< class TypeID > void f() requires constexpr-andor-requires-expression.

The function you want to constrain doesn't have a template parameter list so you can't use methods 1 and 2. Method 3 essentially generates template parameters. So that leaves method 4.

#include <concepts>

template< class T >
struct A {
void f() { /* do something */ }
void g() requires std::integral<T> { /* do something */ }
void h() requires std::integral<T>;
template< std::integral U = T >
void i() { /* do something */ }
};

template< class T >
void A<T>::h() requires std::integral<T> { /* do something */ }

int main() {
A<double> dblA;
dblA.f();
// dblA.g(); // A<double>::g() is not declared or defined
// dblA.h(); // A<double>::h() is not declared or defined
// dblA.i(); // A<double>::h<double>() is not declared or defined
dblA.i<int>(); // A<double>::h<int>() is declared and defined

A<int> intA;
intA.f();
intA.g(); // A<int>::g() is declared and defined
intA.h(); // A<int>::h() is declared and defined
intA.i(); // A<int>::h<int>() is declared and defined
//intA.i<double>(); // A<int>::h<double>() is not declared or defined

return 0;
}


Related Topics



Leave a reply



Submit