I am trying to write some functionality where I need to save different functions and later extract their arguments' types. So I'm using the function signature as template parameter. But I get somewhat unexpected results.
Here's the code:
#include <functional>
#include <iostream>
template <class T>
struct foo
{
foo()
{
std::cout << "class T" << std::endl;
}
};
template <class Ret, class Arg>
struct foo<Ret(Arg)>
{
foo()
{
std::cout << "Ret(Arg)" << std::endl;
}
};
template <class T>
void save(std::function<T>)
{
new foo<T>();
}
int main(int argc, char* argv[])
{
std::function<void(void)> someFoo;
save(someFoo);
return 0;
}
So if the variable someFoo is a function with type void(void), it instantiates the first template, foo<T>. But if I change it to void(int), than I get the desired specialized template instantiated. Why is that?
In C++, having a void argument is actually the same as having no argument at all (unlike in C, by the way). So it would match a specialization for Ret(), but it can't match a specialization for Ret(Arg).
void(void) is the exact same as void() - the second void is optional and makes no difference.
That's why the first template with no parameters is used.
Related
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
I'd like to call a function AsJson in a unified way irrespective of whether I'm dealing with an instance or a primitive type, or anything else.
I thought I could define an abstract base class which classes could inherit, and then define a template function that calls the AsJson member function when the template is specialized to appropriate classes. For other types, they could just specialize the template function.
Something like this:
#include <iostream>
class IInstrumented {
public:
virtual void AsJson() const = 0;
};
template <typename T>
void AsJson(const T&);
template <typename T>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
class Foo : public IInstrumented {
public:
void AsJson() const override { std::cout << "A Foo!" << std::endl; }
};
template <>
void AsJson(const int& x) {
std::cout << "An integer!" << std::endl;
}
int main() {
Foo foo;
AsJson(foo);
int x = 3;
AsJson(x);
}
Unfortunately, this results in the following linker error:
special.cpp:(.text+0x56): undefined reference to `void AsJson<Foo>(Foo const&)'
Is this approach workable as is? Is the fix something relatively minor, or is an entirely different approach warranted?
Although there is an accepted answer, I thought I would point out why you had the error in the first place.
When compiling
AsJson(foo);
the compiler is looking for this template instantiation:
template <>
void AsJson(const Foo&);
which is not defined(undefined reference error) because
template <typename T>
void AsJson(const T&);
has no implementation. It is not using this template specialization:
template <>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
because it is not the best candidate while deducting type. By changing the code to:
AsJson<IInstrumented>(foo);
You then explicitly tell the compiler which template specialization you want to use. Previous answer assume all types will inherits from IInstrumented which might not be the case hence why the static_assert checking for type was added.
By providing a default implementation, you can avoid the necessity to inherits from IInstrumentable. Adding an assert in there might help catching serializing unknown types.
You should change
template <typename T>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
to
template <typename T>
void AsJson(const T& instrumented) {
instrumented.AsJson();
}
And then:
$ ./bla
A Foo!
An integer!
Now if you want to ensure that T is derived from IInstrumented. You should add (c++11 only) :
template <typename T>
void AsJson(const T& instrumented) {
static_assert(std::is_base_of<IInstrumented, T>::value,
"T must be a descendant of IInstrumented"
);
instrumented.AsJson();
}
Then with
class Bar {
public:
void AsJson() { std::cout << "A Foo!" << std::endl; }
};
The following code:
Bar bar;
AsJson(bar);
raises:
bla.cpp: In instantiation of ‘void AsJson(const T&) [with T = Bar]’:
bla.cpp:38:13: required from here
bla.cpp:13:3: error: static assertion failed: T must be a descendant of IInstrumented
static_assert(std::is_base_of<IInstrumented, T>::value,
See also How to ensure that the template parameter is a subtype of a desired type?
This question is related to this one except that rather than dealing with typename template parameters, I am trying to use an enum non-type template parameter.
Is it possible to have a templated (class member function) with only specializations, no general (working) definition in the case of non-type template parameter?
I was able to get one version working, by declaration in the class body and providing specializations only, but any misuse calling with a non-defined template parameter doesn't produce an error until linking. What's worse is the missing symbol cryptically refers to the enum's integral value and not its name, so it would be confusing to other developers.
I was able to get the BOOST_STATIC_ASSERT technique from the referenced question to work for typename template parameter only.
This code demonstrates the idea. I don't want the CAT-version call to compile:
#include <iostream>
#include <boost/static_assert.hpp>
// CLASS HEADER FILE:
struct foo_class
{
enum AllowedTypes { DOG, CAT };
template <AllowedTypes type>
void add_one_third( double bar ) const
{
BOOST_STATIC_ASSERT_MSG(sizeof(type)==0, "enum type not supported.");
}
};
// CLASS SOURCE FILE
template<>
void foo_class::add_one_third<foo_class::DOG>( double bar ) const
{
std::cout << "DOG specialization: " << bar + 1./3. << std::endl;
}
// USER SOURCE FILE
int main()
{
std::cout << "Template Specialization!\n\n";
foo_class a;
a.add_one_third<foo_class::DOG>(3.0); // should succeed
// Compilation fails with or without the following line:
a.add_one_third<foo_class::CAT>(3.0); // should fail at compile-time
return 0;
}
Background:
I have a class member function that takes an enum "ArgType" and a name.
void declareKernelArgument( ArgType type, std::string name );
The definition has turned into an if..else..if..else list for the half-dozen or so allowed ArgType cases. I also have to have final case that throws an exception for an not-allowed ArgType. I'm thinking it would be cleaner to move ArgType to a template parameter, and provide a specialization for each allowed ArgType. Misuse would be caught at compile-time.
With partial specialization of a structure inside the class:
#include <iostream>
class foo_class
{
public:
enum AllowedTypes { T_DOUBLE, T_INT };
private:
template <AllowedTypes type, typename T>
struct AddOneThird;
template <typename T>
struct AddOneThird<T_DOUBLE, T> {
static void apply(T bar) {
std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
}
};
public:
template <AllowedTypes type>
void add_one_third( double bar ) const {
AddOneThird<type, double>::apply(bar);
}
};
int main() {
foo_class a;
a.add_one_third<foo_class::T_DOUBLE>(3.0);
// error: incomplete type ‘foo_class::AddOneThird<(foo_class::AllowedTypes)1u
// a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
return 0;
}
With full specialization of a (friend) class:
#include <iostream>
class foo_class
{
public:
enum AllowedTypes { T_DOUBLE, T_INT };
// if needed
// template<AllowedTypes> friend struct AddOneThird;
public:
template <AllowedTypes type> void add_one_third( double bar ) const;
};
template <foo_class::AllowedTypes>
struct AddOneThird;
template <>
struct AddOneThird<foo_class::T_DOUBLE> {
static void apply(double bar) {
std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
}
};
template <foo_class::AllowedTypes type>
void foo_class::add_one_third( double bar) const {
AddOneThird<type>::apply(bar);
}
int main() {
foo_class a;
a.add_one_third<foo_class::T_DOUBLE>(3.0);
// error: incomplete type ‘AddOneThird<(foo_class::AllowedTypes)1u>’ used
// in nested name specifier
//a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
return 0;
}
Utilizing C++11 or boost::enable_if:
#include <iostream>
#include <type_traits>
class foo_class
{
public:
enum AllowedTypes { T_DOUBLE, T_INT };
template <AllowedTypes type>
typename std::enable_if<type == T_DOUBLE>::type
add_one_third( double bar ) const {
std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
}
};
int main() {
foo_class a;
a.add_one_third<foo_class::T_DOUBLE>(3.0);
// error: no matching function for call to ‘foo_class::add_one_third(double)’
//a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
return 0;
}
From Herb Sutter
It's a lot less intuitive to specialize function templates. For one thing, you can't partially specialize them -- pretty much just because the language says you can't.[2] For another thing, function template specializations don't overload. This means that any specializations you write will not affect which template gets used, which runs counter to what most people would intuitively expect. After all, if you had written a nontemplate function with the identical signature instead of a function template specialization, the nontemplate function would always be selected because it's always considered to be a better match than a template.
If you're writing a function template, prefer to write it as a single function template that should never be specialized or overloaded, and implement the function template entirely in terms of a class template. This is the proverbial level of indirection that steers you well clear of the limitations and dark corners of function templates. This way, programmers using your template will be able to partially specialize and explicitly specialize the class template to their heart's content without affecting the expected operation of the function template. This avoids both the limitation that function templates can't be partially specialized, and the sometimes surprising effect that function template specializations don't overload. Problem solved.
Your enum type sizeof is not 0, change that to 4 at least. Otherwise this will not work. A enum element size is not 0.
Without that everything runs
#include <iostream>
struct foo_class
{
enum AllowedTypes { DOG, CAT };
template <AllowedTypes type>
void add_one_third( double bar ) const
{
std::cout << "YES" << std::endl;
}
};
template<>
void foo_class::add_one_third<foo_class::DOG>( double bar ) const
{
std::cout << "DOG specialization: " << bar + 1./3. << std::endl;
}
int main()
{
std::cout << "Template Specialization!\n\n";
foo_class a;
a.add_one_third<foo_class::DOG>(3.0); // should succeed
// Compilation fails with or without the following line:
//a.add_one_third<foo_class::CAT>(3.0); // should fail at compile-time
return 0;
}
The main difference between the enum case and the referenced question using a typename parameter is that the default definition will be compiled for any use. So, a working solution is as simple as modifying the BOOST_STATIC_ASSERT condition to check allowed enum values.
#include <iostream>
#include <stdexcept>
#include <boost/static_assert.hpp>
// CLASS HEADER FILE:
struct foo_class
{
enum AllowedTypes { DOG, CAT, MOUSE };
template <AllowedTypes type>
void give_bath() const
{
// compile fails if ever attempting to use this function with CAT parameter.
BOOST_STATIC_ASSERT_MSG( (type==DOG) || (type==MOUSE) , "enum type not supported.");
throw std::runtime_error("Unexpected. Above list inconsistent with specializations.");
}
};
// CLASS SOURCE FILE
template<>
void foo_class::give_bath<foo_class::DOG>() const
{
std::cout << "DOG is bathed." << std::endl;
}
template<>
void foo_class::give_bath<foo_class::MOUSE>() const
{
std::cout << "MOUSE is bathed." << std::endl;
}
// USER SOURCE FILE
int main()
{
std::cout << "Template Specialization!\n\n";
foo_class a;
a.give_bath<foo_class::DOG>(); //success
a.give_bath<foo_class::MOUSE>(); // success
// Compilation fails with the following line:
//a.give_bath<foo_class::CAT>(); // fails at compile-time as intended.
return 0;
}
Of course, the whole design smells bad and could likely be handled more elegantly with AllowedTypes being a struct/class with inherited specializations. But this gets to the question at hand.
I'm trying to specialize just one method of a class template.
The accepted answer there definitely does work, but what does this code mean to the compiler and why doesn't it work as expected?
#include <stdio.h>
template <typename T>
struct Node
{
void split() { puts( "Default method" ) ; }
// this compiles, but it doesn't appear to do anything!
template <int> void split() { puts( "Int method" ) ; }
} ;
// This definitely works
//template <> void Node<int>::split() { puts( "Int method" ) ; }
int main()
{
Node<double> n ;
n.split() ; // "Default method"
Node<int> i;
i.split(); // "Default method" again!
}
Your variation defines a seperate template function split that takes an integral (value) template parameter, not a specialization.
To call it you'd have to use e.g.:
n.split<1>();
Call like this:
n.split<10>();
i.split<10>(); //or use any constant integral value
If you intend to call the function template. Note that the function template is not a specialization. It rather is a primary function template, and to call it, you've to provide the template argument explicitly.
You can do even this:
const int x = 100;
i.split<x>(); //ok - x is constant expression
But this is not allowed:
int y = 100;
i.split<y>(); //error - y is not constant expression
Let's look at the syntax for template specialization. First, here's a template
template<typename T> void bar() { std::cout << "generic\n"; }
Now, to declare a specialization, you have to provide a different definition for this template, with the template parameters 'filled in' for the template being specialized:
void bar<int>() { std::cout << "special for int\n"; }
except the above isn't legal because a template specialization must also be a template, with it's own parameters that are not the same as those of the template being specialized. In this case we don't have any other parameters so we have to just use an empty template parameter list:
template<>
void bar<int>() { std::cout << "special for int\n"; }
Note that when 'filling in' the template parameters the specific types go in the same place as they do when you use the template, not inside the template<...>
Here's the code from that other question on how to specialize one member of a class template for a specific intantiation of the template.
template <typename T>
struct Node
{
void split() { puts( "Default method" ) ; }
};
template<>
void Node<int>::split() { puts( "Int method" ) ; }
This is the same as
template <typename T>
struct Node
{
void split();
};
// A
template<typename T>
void Node<T>::split() { puts( "Default method" ) ; }
// B
template<>
void Node<int>::split() { puts( "Int method" ) ; }
As you can see, B is a specialization of A because the Node<int> in B fills in the parameters for A.
Now look at your code:
template <typename T>
struct Node
{
void split() { puts( "Default method" ) ; }
template <int> void split() { puts( "Int method" ) ; }
};
There's no specialization here. Nothing is 'filling in' template parameters for some other template.
So what does template<int> mean? At first glance you might think that it means you're 'filling in' some template<typename T> with T = int, but that is not the case. When you fill in template parameters for specializtion, you don't do it inside the template<...>.
Instead, template<int> is using something called "non-type template parameters". In template<typename T> you're creating a template that can be instantiated for different types. E.g.:
template <typename T> void bar();
bar<Foo>();
bar<std::string>();
template<int> is the same thing, except it lets you get different instantiations for different int values, rather than different types. See:
template <int N> void baz();
baz<1>();
baz<INT_MAX>();
In the one case the template parameter is a typename and so the template is instantiated with types. In the other case the template parameter is int and so the template is instantiated with int values.
Here's an example of a template that uses non-type template parameters:
template<int N>
struct array{
double arr[N];
};
array<3> k = {{ 0.0, 0.0, 1.0 }}; // has a member 'double arr[3]'
array<2> j = {{ 0.0, 1.0 }}; // has a member 'double arr[2]'
And here's another where the value N is deduced:
template<typename T,int N>
int size_of_array(T (&my_array)[N]) { // N is deduced from the argument you pass size_of_array
return N;
}
double X[100];
std::cout << size_of_array(X) << '\n'; // instantiates size_of_array<double,100>
Your class now has two different split methods in it, one templated and one not. If you want to see the output of the template method you have to call it explicitly.
i.split<1>();
Go back to the question you linked, that's the proper way to do it.
The below code couldn't pass the compilation, what's the consideration for this compiler error?
template<class T> void f(T t) {};
template<> void f<char>(char c = 'a') {}
Error message: Default arguments are not allowed on an explicit specialization of a function template
I think that the rationale behind this error is due to the fact that the default arguments in the function template apply to its specialization as well and you are not allowed to define the default argument more than once in C++.
Consider the following:
#include <iostream>
template<class T> void f(T t = 'a') {}
template<> void f<char>(char c)
{
std::cout << c << std::endl;
}
int main(int argc, char **argv)
{
f<char>();
}
This will print a meaning that specialization is called with the default argument defined in the main template.
If you need a different default argument for each specialization you can use the approach illustrated below:
#include <iostream>
template<class T>
struct default_arg
{
static T get() { return T(); }
};
template<class T> void f(T t = default_arg<T>::get()) {}
template<>
struct default_arg<char>
{
static char get() { return 'a'; }
};
template<> void f<char>(char c)
{
std::cout << c << std::endl;
}
int main(int argc, char **argv)
{
f<char>();
}
C++98 §12.7/21 "Default function arguments shall not be specified in ... the explicit specialization of a function template".
Regarding rationale I think it has to do with a call always being resolved against the primary template. A call that leaves out an argument required by the primary template, couldn't be resolved wihtout changing the lookup rules.
The particular template instance used is decided by the type of the function argument. Thus, in this case, the explicit specialization is selected by the providing a char argument; it is only at this point that the default argument would come into play, where it is now redundant.
It only makes sense to provide default arguments on the template declaration itself. The disadvantage is that you have to specify the appropriate specialization yourself, namely f<char>(), which removes some of the advantages in using a default argument in the first place.
To achieve the behavior that, I believe, you want, use the following:
template<class T> void f(T t) {}
template<> void f<char>(char c) {}
void f() { f('a'); }