I need to specialize template member function for some type (let's say double). It works fine while class X itself is not a template class, but when I make it template GCC starts giving compile-time errors.
#include <iostream>
#include <cmath>
template <class C> class X
{
public:
template <class T> void get_as();
};
template <class C>
void X<C>::get_as<double>()
{
}
int main()
{
X<int> x;
x.get_as();
}
here is the error message
source.cpp:11:27: error: template-id
'get_as<double>' in declaration of primary template
source.cpp:11:6: error: prototype for
'void X<C>::get_as()' does not match any in class 'X<C>'
source.cpp:7:35: error: candidate is:
template<class C> template<class T> void X::get_as()
What is the problem here, and how can I fix it?
It doesn't work that way. You would need to say the following, but it is not correct
template <class C> template<>
void X<C>::get_as<double>()
{
}
Explicitly specialized members need their surrounding class templates to be explicitly specialized as well. So you need to say the following, which would only specialize the member for X<int>.
template <> template<>
void X<int>::get_as<double>()
{
}
If you want to keep the surrounding template unspecialized, you have several choices. I prefer overloads
template <class C> class X
{
template<typename T> struct type { };
public:
template <class T> void get_as() {
get_as(type<T>());
}
private:
template<typename T> void get_as(type<T>) {
}
void get_as(type<double>) {
}
};
If one is able to used std::enable_if we could rely on SFINAE (substitution failure is not an error)
that would work like so (see LIVE):
#include <iostream>
#include <type_traits>
template <typename C> class X
{
public:
template <typename T,
std::enable_if_t<!std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as T" << std::endl; }
template <typename T,
std::enable_if_t<std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as double" << std::endl; }
};
int main() {
X<int> d;
d.get_as<double>();
return 0;
}
The ugly thing is that, with all these enable_if's only one specialization needs to be available for the compiler otherwise disambiguation error will arise. Thats why the default behaviour "get as T" needs also an enable if.
Probably the cleanest way to do this in C++17 and on-wards is to use a if constexpr in combination with the std::is_same_v type trait without explicitly specialisation at all:
#include <iostream>
#include <type_traits>
template <typename C>
class X {
public:
template <typename T>
void get_as() {
// Implementation part for all types
std::cout << "get as ";
// Implementation part for each type separately
if constexpr (std::is_same_v<double, T>) {
std::cout << "'double'";
} else if constexpr (std::is_same_v<int, T>) {
std::cout << "'int'";
} else {
std::cout << "(default)";
}
// Implementation part for all types
std::cout << std::endl;
return;
}
};
int main() {
X<int> d {};
d.get_as<double>(); // 'double'
d.get_as<int>(); // 'int'
d.get_as<float>(); // (default)
return EXIT_SUCCESS;
}
Try it here!
If you need to have a return type as well you could declare the return type as auto:
template <typename T>
auto get_as() {
if constexpr (std::is_same_v<double, T>) {
return 0.5;
} else {
return 0;
}
}
Related
my problem is the following, I have a template object, and in this object a method also template, for which I want to make a specialization, only the compiler always returns an error: "a declaration of model containing a list of model parameters can not be followed by an explicit specialization declaration.
I would like to understand in this case if how to specialize the method, here is the code:
template<typename T>class Foo
{
public:
template<typename T2> Foo<T2> cast(void);
};
template<typename T> template<typename T2> Foo<T2> Foo<T>::cast(void)
{
Foo<T2> tmp;
std::cout << "1" << std::endl;
return tmp;
}
template<typename T> template<> Foo< int > Foo<T>::cast< int >(void)
{
Foo<int> tmp;
std::cout << "2" << std::endl;
return tmp;
}
int main()
{
Foo<double> bar();
bar.cast<int>();
}
The problem is that we can't fully specialize the member function template without also fully specializing the class template also. This means that the correct syntax would be as shown below:
template<typename T> struct CompressVector
{
template<typename T2> CompressVector<T2> cast();
};
template<typename T> template<typename T2> CompressVector<T2> CompressVector<T>::cast()
{
CompressVector<T2> tmp;
//other code here
return tmp;
}
//vvvvvvvvvv-vvvvvvvvv-------------------------------------vvv------------>made changes here
template<> template< > CompressVector<int> CompressVector<int>::cast< int >(void)
{
CompressVector<int> tmp;
//other code here
return tmp;
}
Working demo
I want to specialise a single template method in a non-template class to use an std::vector however only the return type of the method uses the template.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
template<>
int Foo::Get()
{
std::cout << "int" << std::endl;
return 12;
}
template<typename T>
std::vector<T> Foo::Get()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
}
This compiles with an error indicating that the std::vector attempted specialisation does not match any prototype of Foo, which is completely understandable.
In case it matters, use of C++14 is fine and dandy.
You can only partially specialize classes (structs) (cppreference) - so the way to overcome your problems is to add helper struct to allow this partial specialization of std::vector<T> - e.g. this way:
class Foo
{
private: // might be also protected or public, depending on your design
template<typename T>
struct GetImpl
{
T operator()()
{
std::cout << "generic" << std::endl;
return T();
}
};
public:
template<typename T>
auto Get()
{
return GetImpl<T>{}();
}
};
For int - you can fully specialize this function:
template<>
int Foo::GetImpl<int>::operator()()
{
std::cout << "int" << std::endl;
return 12;
}
For std::vector<T> you have to specialize entire struct:
template<typename T>
struct Foo::GetImpl<std::vector<T>>
{
std::vector<T> operator()()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
Partial specialisation of template functions (including member functions) is not allowed. One option is to overload instead using SFINAE. For example,
/// auxiliary for is_std_vetor<> below
struct convertible_from_std::vector
{
template<typename T>
convertible_from_std::vector(std::vector<T> const&);
};
template<typename V>
using is_std_vector
= std::is_convertible<V,convertible_from_std_vector>;
class Foo
{
public:
template<typename T, std::enable_if_t< is_std::vector<T>::value,T>
Get()
{
std::cout << "vector" << std::endl;
return T();
}
template<typename T, std::enable_if_t<!is_std::vector<T>::value,T>
Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
Note that the helper class is_std_vector may be useful in other contexts as well, so it worth having somewhere. Note further that you can make this helper class more versatile by asking for any std::vector or specific std::vector<specific_type, specific_allocator>. For example,
namespace traits {
struct Anytype {};
namespace details {
/// a class that is convertible form C<T,T>
/// if either T==AnyType, any type is possible
template<template<typename,typename> C, typename T1=Anytype,
typename T2=Anytype>
struct convCtTT
{
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T1=Anytype>
struct convCtTT<C,T1,AnyType>
{
template<typename T2>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T2=Anytype>
struct convCtTT<C,AnyType,T2>
{
template<typename T1>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C>
struct convCtTT<C,AnyType,AnyType>
{
template<typename T1, typename T2>
convCtTT(C<T1,T2> const&);
};
}
template<typename Vector, typename ValueType=AnyType,
typename Allocator=AnyType>
using is_std_vector
= std::is_convertible<Vector,details::convCtTT<std::vector,ValueType,
Allocator>;
}
You can't partially specialze template in c++. You need to overload your function and pass the type in parameters.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
return this->getTemplate(static_cast<T*>(0)); //
}
private:
template<class T> T getTemplate(T* t)
{
std::cout << "generic" << std::endl;
return T();
}
template<class T> std::vector<T> getTemplate(std::vector<T>* t)
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
template <> int Foo::getTemplate(int* t)
{
std::cout << "int" << std::endl;
return 12;
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
auto v = foo.Get<std::vector<int>>();
}
Edit : fixed a typo in the code
I'm trying to specialize a member function template for two different types of classes as follows:
#include <iostream>
#include <boost/utility/enable_if.hpp>
struct Wibble
{
static const bool CAN_WIBBLE = true;
};
struct Wobble
{
static const bool CAN_WIBBLE = false;
};
struct Foo
{
//template<typename T> // Why isn't this declaration sufficient?
//void doStuff();
template<typename T>
typename boost::enable_if_c<T::CAN_WIBBLE,void>::type
doStuff();
template<typename T>
typename boost::enable_if_c<!T::CAN_WIBBLE,void>::type
doStuff();
};
template<typename T>
typename boost::enable_if_c<T::CAN_WIBBLE,void>::type
Foo::doStuff()
{
std::cout << "wibble ..." << std::endl;
}
template<typename T>
typename boost::enable_if_c<!T::CAN_WIBBLE,void>::type
Foo::doStuff()
{
std::cout << "I can't wibble ..." << std::endl;
}
int main()
{
Foo f;
f.doStuff<Wibble>();
f.doStuff<Wobble>();
}
Whereas GCC 4.8.2 compiles the code, VS .NET 2008 spits out the error message:
error C2244: 'Foo::doStuff' : unable to match function definition to an existing declaration
definition
'boost::enable_if_c<!T::CAN_WIBBLE,void>::type Foo::doStuff(void)'
existing declarations
'boost::enable_if_c<!T::CAN_WIBBLE,void>::type Foo::doStuff(void)'
'boost::enable_if_c<T::CAN_WIBBLE,void>::type Foo::doStuff(void)'
I suggest to use tag dispatching: https://ideone.com/PA5PTg
struct Foo
{
template<bool wibble>
void _doStuff();
public:
template<typename T>
void doStuff()
{
_doStuff<T::CAN_WIBBLE>();
}
};
template<>
void Foo::_doStuff<true>() { std::cout << "wibble ..." << std::endl; }
template<>
void Foo::_doStuff<false>() { std::cout << "I can't wibble ..." << std::endl; }
You can't partially specialize (member) function templates. End of story.
Even if you could, you should have had a SFINAE-friendly primary template. In pseudo code:
template<typename T, typename Enable> void doStuff();
template<typename T> void doStuff<T, typename boost::enable_if_c<T::CAN_WIBBLE,void>::type>()
{ std::cout << "wibble ..." << std::endl; }
template<typename T> void doStuff<T, typename boost::enable_if_c<!T::CAN_WIBBLE,void>::type>()
{ std::cout << "I can't wibble ..." << std::endl; }
You could still use this technique if you are ready class templates (as functors or just types defining non-template methods...).
As a rule of thumb, for function templates, overload resolution provides static polymorphism that removes the need for partial specialization. See
GotW #49 Template Specialization and Overloading
Why Not Specialize Function Templates?
Both by Herb Sutter
I am trying to port some C++ code from Windows to Solaris(Unix). There are some template code need to be changed. I am using Solaris' compiler CC, g++ should have same issue.
I have a particular part of code introduce some trouble. They are simplified as following:
#include <exception>
#include <cmath>
#include <string>
#include <iostream>
// define the "not implement" error
class tempException: public std::exception
{
public:
virtual const char* what() const throw()
{
return "not been implemented!";
}
} nondeferr;
// the template class
template <typename T>
class A
{
public:
template <typename Val>
Val getValue(T t) { throw nondeferr; }
template<>
double getValue(T t) { return exp( 1.5 * t ); } //Specialize the getValue for double type.
};
// test code
int main()
{
try
{
A<int> testA;
std::cout << testA.getValue<double>(2) << std::endl;
std::cout << testA.getValue<std::string>(2) << std::endl;
}
catch (tempException& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
To compile this sample code in UNIX, the compilation error comes out as the explicit specialization cannot be in the class A scope.
Here the getValue function only different from the return type, so we cannot modify it using the overload way.
And for some reason, change class A with simple template variable T to class A with double template variables T and Val is not allowed. It will introduce a lots of changes when we try to use this basic class.
May I know if there is any solution? I am currently remove the getValue function, replace it as getDoubleValue... But that is not so good too.
For those who interested, now the class A looks like this:
template <typename T>
class A
{
public:
// the Get Value we want
template <typename R>
R getValue(T t) { return get_value_impl<R>::apply(*this, t); }
// the general get value struct
template<typename R, typename = void>
struct get_value_impl
{
static R apply(A a, T t) { throw nondeferr; }
};
// partial specialization, which is allowed in std C++
template <typename S>
struct get_value_impl<double, S>
{
static double apply(A a, T t) { return exp( 1.5 * t ); }
};
};
The logic behind is explicit specialization is not allowed in standard. However, partial specialization is allowed. Thanks Anycorn again for the splendid solution.
// the template class
template <typename T>
class A {
template<>
double getValue(T t) { return exp( 1.5 * t ); }
};
This isnt allowed by standard.
do:
template <typename T>
class A {
template<class R>
R getValue(T t) { return get_value_impl<double>::apply(*this, t); }
template<class R, class = void>
struct get_value_impl; // specialize this
};
It is not allowed to specialize a member function without specializing the surrounding class. Visual Studio allows this as an extension.
I need to specialize template member function for some type (let's say double). It works fine while class X itself is not a template class, but when I make it template GCC starts giving compile-time errors.
#include <iostream>
#include <cmath>
template <class C> class X
{
public:
template <class T> void get_as();
};
template <class C>
void X<C>::get_as<double>()
{
}
int main()
{
X<int> x;
x.get_as();
}
here is the error message
source.cpp:11:27: error: template-id
'get_as<double>' in declaration of primary template
source.cpp:11:6: error: prototype for
'void X<C>::get_as()' does not match any in class 'X<C>'
source.cpp:7:35: error: candidate is:
template<class C> template<class T> void X::get_as()
What is the problem here, and how can I fix it?
It doesn't work that way. You would need to say the following, but it is not correct
template <class C> template<>
void X<C>::get_as<double>()
{
}
Explicitly specialized members need their surrounding class templates to be explicitly specialized as well. So you need to say the following, which would only specialize the member for X<int>.
template <> template<>
void X<int>::get_as<double>()
{
}
If you want to keep the surrounding template unspecialized, you have several choices. I prefer overloads
template <class C> class X
{
template<typename T> struct type { };
public:
template <class T> void get_as() {
get_as(type<T>());
}
private:
template<typename T> void get_as(type<T>) {
}
void get_as(type<double>) {
}
};
If one is able to used std::enable_if we could rely on SFINAE (substitution failure is not an error)
that would work like so (see LIVE):
#include <iostream>
#include <type_traits>
template <typename C> class X
{
public:
template <typename T,
std::enable_if_t<!std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as T" << std::endl; }
template <typename T,
std::enable_if_t<std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as double" << std::endl; }
};
int main() {
X<int> d;
d.get_as<double>();
return 0;
}
The ugly thing is that, with all these enable_if's only one specialization needs to be available for the compiler otherwise disambiguation error will arise. Thats why the default behaviour "get as T" needs also an enable if.
Probably the cleanest way to do this in C++17 and on-wards is to use a if constexpr in combination with the std::is_same_v type trait without explicitly specialisation at all:
#include <iostream>
#include <type_traits>
template <typename C>
class X {
public:
template <typename T>
void get_as() {
// Implementation part for all types
std::cout << "get as ";
// Implementation part for each type separately
if constexpr (std::is_same_v<double, T>) {
std::cout << "'double'";
} else if constexpr (std::is_same_v<int, T>) {
std::cout << "'int'";
} else {
std::cout << "(default)";
}
// Implementation part for all types
std::cout << std::endl;
return;
}
};
int main() {
X<int> d {};
d.get_as<double>(); // 'double'
d.get_as<int>(); // 'int'
d.get_as<float>(); // (default)
return EXIT_SUCCESS;
}
Try it here!
If you need to have a return type as well you could declare the return type as auto:
template <typename T>
auto get_as() {
if constexpr (std::is_same_v<double, T>) {
return 0.5;
} else {
return 0;
}
}