I'm trying to specialize a method of a (non-templated!) class. Apparently, it's not possible, however I am struggling to figure out why, or how to overcome the problem.
class MyClass {
public:
template <typename... T>
auto MyMethod(T... t) -> void { std::cout << "Original" << std::endl; }
template <typename... T>
auto MyMethod<int, T...>(int value, T... t) -> void { std::cout << "Specialization" << value << std::endl; }
};
int main(void) {
MyClass myClass;
myClass.MyMethod<char, char>('c', 'c');
myClass.MyMethod<int, char>(123, 'c');
return 0;
}
The error is as follows:
test.cpp:10:49: error: non-class, non-variable partial specialization ‘MyMethod<int, T ...>’ is not allowed
10 | auto MyMethod<int, T...>(int value, T... t) -> void { std::cout << "Specialization" << value << std::endl; }
It seems that there would be no reason for this to be impossible (with a template class it makes sense that the entire class has to be specialized too). What am I missing? Is it literally not possible?
Only classes can be partially specialized; methods can only be fully specialized. As your methods still have template arguments (T) that are not specified, this means a partial method specialization.
If you would use these template arguments for a class (and call a non-templated member function of that class) then this should be possible.
Related
I am trying to figure out how to "overload" a variadic function template with a "more specialized" variadic function template. For example:
#include <iostream>
template <typename... ARGS_>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
struct Test {
};
template <typename... ARGS_>
void foo(void(*fn)(ARGS_..., Test*)) {
std::cout << "Test function pointer foo" << std::endl;
}
void test1(int a, int b) {
std::cout << "test1()" << std::endl;
}
void test2(int a, int b, Test* x) {
std::cout << "test2()" << std::endl;
}
int main() {
foo(&test1);
foo(&test2);
return 0;
}
The output of this code is:
Generic function pointer foo
Generic function pointer foo
Rather than:
Generic function pointer foo
Test function pointer foo
as I want.
Conceptually, I am trying to notate "Use template method A if you have any type(s) of arguments where the LAST on is Test* and use template method B if the last type is NOT Test*."
What is the correct method to accomplish this type of behavior?
SFINAE'd overloads based on the last parameter pack argument
You can add mutually exclusive overloads based on whether the last type in the variadiac parameter pack is Test* or not:
#include <type_traits>
template <typename... Ts>
using last_t = typename decltype((std::type_identity<Ts>{}, ...))::type;
struct Test {};
template <
typename... ARGS_,
std::enable_if_t<!std::is_same_v<last_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
template <
typename... ARGS_,
std::enable_if_t<std::is_same_v<last_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
// Special case for empty pack (as last_t<> is ill-formed)
void foo(void (*fn)()) { std::cout << "Zero args" << std::endl; }
making use if C++20's std::type_identity for the last_t transformation trait.
Used as:
void test1(int, int b) {}
void test2(int, int b, Test *x) {}
void test3(Test *) {}
void test4() {}
int main() {
foo(&test1); // Generic function pointer foo
foo(&test2); // Test function pointer foo
foo(&test3); // Test function pointer foo
foo(&test4); // Zero args
}
Avoiding the zero-arg special case as an overload?
The zero-arg foo overload can be avoided in favour of tweaking the last_t trait into one which also accepts an empty pack, such that a query over the empty pack is used to resolve to the generic overload. Neither its semantics nor its implementation becomes as straight-forward and elegant, however, as "the last type in an empty type list" does not make much sense, meaning the trait need to be tweaked into something different:
template <typename... Ts> struct last_or_unique_dummy_type {
using type = typename decltype((std::type_identity<Ts>{}, ...))::type;
};
template <> class last_or_unique_dummy_type<> {
struct dummy {};
public:
using type = dummy;
};
template <typename... Ts>
using last_or_unique_dummy_type_t =
typename last_or_unique_dummy_type<Ts...>::type;
template <typename... ARGS_,
std::enable_if_t<!std::is_same_v<
last_or_unique_dummy_type_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
template <typename... ARGS_,
std::enable_if_t<std::is_same_v<last_or_unique_dummy_type_t<ARGS_...>,
Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
Using an additional overload for the empty pack is likely the least surprising approach.
C++20 and the identity_t trick
In case you are not yet at C++20, an identity meta-function is trivial to write yourself:
template <typename T>
struct type_identity {
using type = T;
};
Any specialization of this class template, unless partially/explicitly specialized otherwise (which is UB for the STL type), is trivial and default-constructible. We leverage this in the definition of last_t above: default-constructing a series of trivial types in an unevaluated context, and leveraging that the last of those types embeds the input to the identity trait whose specialization is that trivial type, and whose wrapped alias declaration type is the type of the last parameter in the variadic parameter pack.
Check that the last element of ARGS... is Test*.
It won't do that for you this way. One ways is:
template<class...Ts>
struct last_type {};
template<class T1, class T2, class...Ts>
struct last_type<T1, T2, Ts...>:last_type<T2, Ts...>{};
template<class T>
struct last_type<T>{
using type=T;
};
template<class...Ts>
using last_type_t = typename last_type<Ts...>::type;
now you just:
template <typename... ARGS_>
requires std::is_same_v<last_type_t<ARGS_...>, Test*>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
Live example.
Without concepts, you have to replace that requires clause:
template <typename... ARGS_,
std::enable_if_t<std::is_same_v<last_type_t<ARGS_...>, Test*>, bool> = true
>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
which is a more obscure "cargo cult" way to basically say the same thing. (you also need to invert enable if clause in the other overload; not = false but !, and handle 0 arg case (prepend void on the type list?))
The reason why your attempt doesn't work is that C++ makes ... matching insanely greedy. It is generally not a good idea to put things you want pattern matched behind it in a context where pattern matching of parameters is done.
I am having this template matching error. I know partial specialization is not allowed in for function template, but I think it should work with full specialization. What change do I need to make to fix this issue ? Thanks.
#include <iostream>
template<typename T>
void allocate(){
std::cout << "default" << std::endl;
}
template<>
void allocate<int>() {
std::cout << "int" << std::endl;
}
template<>
void allocate<double>() {
std::cout << "double" << std::endl;
}
int main()
{
allocate(); // Compiler error, I expect this should match the first template function.
allocate<int>();
allocate<double>();
return 0;
}
You need to specify the template argument explicitly, the template parameter T can't be deduced from the context. e.g.
allocate<void>();
Or specify default argument for the template parameter, e.g.
template<typename T = void>
void allocate(){
std::cout << "default" << std::endl;
}
then you can call it as
allocate(); // T is void
The primary template needs the template parameter to be specified explicitly, so you can do:
allocate<struct T>(); // ok
and since T is a new type named only for the purpose of this call, there is guaranteed to not be a specialization for this type, and the primary template will be called.
You could also give a default type for the template parameter in the primary:
template<typename T = struct Default>
void allocate(){
std::cout << "default" << std::endl;
}
and again, no specialization can exist for Default since this type only exists in the scope of the primary template.
Now you can call the primary template without template parameters:
allocate();
I want to create a generic class containing a method displaying one message if the type of the class is int and the other when it's double. Here's my code:
template<class T>
class A {
public:
template <T> void B();
};
template<class T>
void A<int>::B{
//some code here
}
template<class T>
void A<double>::B{
//some code here
}
I got the following errors:
'double': illegal type for non-type template parameter '__formal'
'A<int>::B': unable to match function definition to an existing declaration
Thanks in advance for any solutions.
A couple of things:
There's no reason for B to be a template. You want to specialize for A
B is a method. Methods accept parameters. When defining the method, you omitted the parenthesis ()
Template specialization always involves an empty template parameter <>
Code:
template<class T>
class A {
public:
void B();
};
template<>
void A<int>::B(){
std::cout << "A<int>::B" << std::endl;
}
template<>
void A<double>::B(){
std::cout << "A<double>::B" << std::endl;
}
Demo
If you feel compelled to make B a template, I should note that in general one does not perform template specialization on functions. This is primarily because they cannot be partially specialized, and it's almost always better to write an overload. In your case, B takes no arguments, so there's some argument to be made in favor of specialization.
More often than not, one would use a tag dispatching approach instead, coupled with a helper function so that they can choose their desired function by taking advantage of overloading instead. Here's a simple example of tag dispatching for your case:
template<class T>
class A {
public:
template<class U>
void B()
{
B(ATag<U>{});
}
private:
template<class U>
struct ATag{};
void B(ATag<int>)
{
std::cout << "B<int>" << std::endl;
}
void B(ATag<double>)
{
std::cout << "B<double>" << std::endl;
}
};
tag dispatch demo
Is it possible to overload or specialize class member functions based on given enum values?
enum class Type {
TypeA,
TypeB,
TypeC
};
class Foo {
public:
template <Type t, typename R = std::enable_if_t<t==Type::TypeA, int>>
R get() {
return 1;
}
template <Type t, typename R = std::enable_if_t<t==Type::TypeB, double>>
R get() {
return 2;
}
template <Type t, typename R= std::enable_if_t<t==Type::TypeC, float>>
R get() {
return 3;
}
};
Foo foo;
std::cout << foo.get<Type::TypeA>() << std::endl;
std::cout << foo.get<Type::TypeB>() << std::endl;
std::cout << foo.get<Type::TypeC>() << std::endl;
The compile complain about overloading on above code snippet.
One pretty standard way to fix it is to put the std::enable_if clause in the return type of the function, rather than in the template parameters like that.
This compiles for me at c++11 standard.
#include <iostream>
#include <type_traits>
enum class Type {
TypeA,
TypeB,
TypeC
};
class Foo {
public:
template <Type t>
typename std::enable_if<t==Type::TypeA, int>::type get() {
return 1;
}
template <Type t>
typename std::enable_if<t==Type::TypeB, double>::type get() {
return 2;
}
template <Type t>
typename std::enable_if<t==Type::TypeC, float>::type get() {
return 3;
}
};
static_assert(std::is_same<int, decltype( std::declval<Foo>().get<Type::TypeA>())>::value, "");
static_assert(std::is_same<double, decltype( std::declval<Foo>().get<Type::TypeB>())>::value, "");
static_assert(std::is_same<float, decltype( std::declval<Foo>().get<Type::TypeC>())>::value, "");
int main() {
Foo foo;
std::cout << foo.get<Type::TypeA>() << std::endl;
std::cout << foo.get<Type::TypeB>() << std::endl;
std::cout << foo.get<Type::TypeC>() << std::endl;
}
I'm not sure if I can explain in detail why this change makes such a difference for the compiler.
However, consider the following. With your version, although you are never actually instantiating get with two explicit template parameters, technically all three of those member functions templates can "collide" and produce functions with exactly the same name. Because, if you did instantiate get<Type::TypeB, int>, then it would have the same return type, input parameters, and name as get<Type::TypeA>. C++ does not support function template specialization, it would make overload resolution rules very complicated. So having function templates with the potential to collide like this can make the compiler very upset.
When you do it the way I showed, there is no possibility that the templates can collide and produce a function with the same name and signature.
You can avoid the use of std::enable_if mapping the Type and return types specializing a simple struct (Bar, in the following example)
#include <iostream>
enum class Type {
TypeA,
TypeB,
TypeC
};
template <Type t>
struct Bar;
template <>
struct Bar<Type::TypeA>
{ using type = int; };
template <>
struct Bar<Type::TypeB>
{ using type = double; };
template <>
struct Bar<Type::TypeC>
{ using type = float; };
class Foo {
public:
template <Type t>
typename Bar<t>::type get();
};
template <>
Bar<Type::TypeA>::type Foo::get<Type::TypeA> ()
{ return 1; }
template <>
Bar<Type::TypeB>::type Foo::get<Type::TypeB> ()
{ return 2.2; }
template <>
Bar<Type::TypeC>::type Foo::get<Type::TypeC> ()
{ return 3.5f; }
int main ()
{
Foo foo;
std::cout << foo.get<Type::TypeA>() << std::endl;
std::cout << foo.get<Type::TypeB>() << std::endl;
std::cout << foo.get<Type::TypeC>() << std::endl;
return 0;
}
Your example doesn't compile because defaulted type templated parameters are not part of the function signature. So as far as the compiler is concerned, you are defining the same function multiple times, which is illegal. Instead, you need to use a defaulted non-type template parameter.
class Foo {
public:
template <Type t, std::enable_if_t<t==Type::TypeA, int> = 0>
int get() {
return 1;
}
template <Type t, std::enable_if_t<t==Type::TypeB, int> = 0>
double get() {
return 2;
}
template <Type t, std::enable_if_t<t==Type::TypeC, int> = 0>
float get() {
return 3;
}
};
Two working solutions are already posted; so why did I post this one? Well, I like it better. To be concrete:
It keeps what the function actually takes and returns separate from when it's enabled. This makes the function easier to read and understand. You can pull the code snippet from the second argument into a tiny macro called REQUIRE or similar; this makes it extremely clear what's going on. Neither of the other two answers posted have this property.
This technique is more universal; it can be used in slightly different situations with constructors, which don't return anything and cannot be used with the other two solutions
max66's approach of mapping the enum to a type is nice and it's something you should be aware of. It's more appropriate though when you can write the body generically; if you have to write out every implementation body separately anyhow, it only adds boilerplate (IMHO). Also you should be aware that a solution based on specialization is fragile; it doesn't work if there's a second template parameter nor if the class Foo is a class template, because of the restrictions on specialization function templates.
With the macro I mentioned above you could write it this way:
template <Type t, REQUIRE(t==Type::TypeA)>
int get() {
return 1;
}
// ...
Which I think is as good as it gets.
I have a visitor class resembling this:
struct Visitor
{
template <typename T>
void operator()(T t)
{
...
}
void operator()(bool b)
{
...
}
};
Clearly, operator()(bool b) is intended to be a specialization of the preceding template function.
However, it doesn't have the template<> syntax that I'm used to seeing before it, declaring this as a template specialization. But it does compile.
Is this safe? Is this correct?
Your code is not a template specialization, but rather a non-templated function. There are some differences there. The non-templated operator() will take precedence over a templated version (for an exact match, but type conversions will not take place there) but you can still force the templated function to be called:
class Visitor
{
public: // corrected as pointed by stefanB, thanks
template <typename T>
void operator()( T data ) {
std::cout << "generic template" << std::endl;
}
void operator()( bool data ) {
std::cout << "regular member function" << std::endl;
}
};
template <> // Corrected: specialization is a new definition, not a declaration, thanks again stefanB
void Visitor::operator()( int data ) {
std::cout << "specialization" << std::endl;
}
int main()
{
Visitor v;
v( 5 ); // specialization
v( true ); // regular member function
v.operator()<bool>( true ); // generic template even if there is a non-templated overload
// operator() must be specified there (signature of the method) for the compiler to
// detect what part is a template. You cannot use <> right after a variable name
}
In your code there is not much of a difference, but if your code needs to pass the template parameter type it will get funnier:
template <typename T>
T g() {
return T();
}
template <>
int g() {
return 0;
}
int g() {
return 1;
}
int main()
{
g<double>(); // return 0.0
g<int>(); // return 0
g(); // return 1 -- non-templated functions take precedence over templated ones
}
What you have here is function overloading; to obtain template specialization, you indeed need the template <> syntax. However, you should be aware that these two approaches, even if they may seem identical, are subtly different, and even the compiler might get lost when choosing the right function to call. Listing all the possible cases would be a little too long for this answer, but you might want to check Herb Sutter GoTW #49 on the subject.
Oh, it'll compile. It just won't be a template function. You'll have a regular non-template function instead of a template specialization.
It's safe, and actually likely what you want as well. The Visitor pattern is normally implemented by overloading. Specializing function templates isn't really a good idea anyway.
What you did is not template serialization, but function overloading. It is safe.
P.S. It's difficult to say whether it's correct or not, without knowing what you're trying to achieve. Keep in mind that no matter is it template or overloaded function, your operator will be chosen in compile time. If you need to run-time dispatch, you need polymorphism, not overloading. Well, you probably know it anyway; just in case.
You have
void operator()(bool b) that is non
templated function
template< typename T > void
operator()(T t) which is a separate
base template that overloads the
above
You could have a full specialization of the second one as in template<> void operator(int i) which would only be considered when void operator()(bool b) did not match.
The specialization of base template is used to select which of the base template methods to call. However in your case you have a non-templated method that will get considered first.
The article Why Not Specialize Function Templates? gives quite good explanation of how the method is selected.
In sumary:
Non template functions are
considered first (this is your plain
operator()(bool) above)
Function base templates get checked
second (this is your templated
function), the most specialized base-template is selected and then if it has specialization for the exact types that specialization is used otherwise the base template is used with 'the correct' types (see explanation in the article)
Example:
#include <iostream>
using namespace std;
struct doh
{
void operator()(bool b)
{
cout << "operator()(bool b)" << endl;
}
template< typename T > void operator()(T t)
{
cout << "template <typename T> void operator()(T t)" << endl;
}
};
// note can't specialize inline, have to declare outside of the class body
template<> void doh::operator()<>(int i)
{
cout << "template <> void operator()<>(int i)" << endl;
}
template<> void doh::operator()<>(bool b)
{
cout << "template <> void operator()<>(bool b)" << endl;
}
int main()
{
doh d;
int i;
bool b;
d(b);
d(i);
}
You get calls to:
operator()(bool b) <-- first non template method that matches
template <> void operator()(int i) <-- the most specialized specialization of templated function is called