I am teaching myself template programming in C++, so some of my assumptions may be wrong - please correct me if you see any errors.
I am trying to use an STL list as a template parameter to a function. The function is supposed to be used with all sorts of data types, so I have defined the function as template<class T> rather than template<template<> class T> in its original declaration. I now want to specialise it to support a template class.
template<class T>
void function(T param)
{
// do something with T
}
template<template <class T, class Allocator> class listPlaceholder>
void function(std::list<T, Allocator> param)
{
// do something with every element in param (I actually need to know it's a list)
std::list<T, Allocator>::iterator current = param.begin();
std::list<T, Allocator>::iterator end = param.end();
do {
function<T>(*current);
} while (++current != end);
}
The problem is that when I try to compile this code (under GCC) it says that T and Allocator are not defined in the scope. My primary question is "how do I specialise for template classes?" and secondly, if it is possible, "how do I extract the template template parameters?".
As mentioned previously, I am learning template programming, so obvious solutions are welcome.
You want to declare those parameters
template<template <class T, class Allocator> class listPlaceholder,
class T, class Allocator>
void function(listPlaceholder<T, Allocator> param)
{
// do something with every element in param (I actually need to know it's a list)
typename listPlaceholder<T, Allocator>::iterator current = param.begin();
typename listPlaceholder<T, Allocator>::iterator end = param.end();
do {
function<T>(*current);
} while (++current != end);
}
The names you used in the formal parameter list have no meaning. You also forgot to use listPlaceholder actually. But I'm assuming that was accidental.
As another poster said, you also need the typename keyword because the names are dependent names.
For why the names in the formal list are meaningless, compare it to function pointers:
void f(void (*p)(int t, int allocator), int t, int allocator) {
p(t, allocator);
}
void g(int a, int b) {
}
int main() {
f(&g, 0, 1);
}
What is important is only the type of the parameters, and I could have written void(*p)(int, int) too. In your case, what is important is only that both parameters are type parameters. So you could have written the template template parameter also as template<class, class> class listPlaceholder too, completely equivalent.
Last but not least, I would like to emphasize that you have not specialized function, but you have overloaded it with another template. So, both functions are two completely different function templates.
g++ is actually correct here; you haven't declared T or Allocator in this scope. The template declaration you have
template<template <class T, class Allocator> class listPlaceholder>
void function(std::list<T, Allocator> param)
Says "I am parameterized over a class template that takes in two classes as arguments." However, the names of those arguments can't be accessed anywhere in the body of the template. They're mostly there as placeholders, and the above template declaration is equivalent to
template<template <class, class> class listPlaceholder>
void function(std::list<T, Allocator> param)
This is similar to how if you were to declare a regular C++ function that took another function as an argument, you can't access the names of the parameters. For example, this is illegal:
void DoSomething(void function(int x, int y)) {
x = 5; // Error!
}
Since it's equivalent to
void DoSomething(void function(int, int)) {
x = 5; // Error!
}
I believe what you want to do is change your template function signature to look like this:
template<class T, class Allocator>
void function(std::list<T, Allocator> param)
This says "This function is parameterized over two types. When provided as an argument a std::list parameterized over a type and an allocator, the body of this function can refer to those types as T and Allocator."
Use typename as:
typename std::list<T, Allocator>::iterator current = param.begin();
typename std::list<T, Allocator>::iterator end = param.end();
Its because iterator is dependent name, so typename is required by the compiler, so that it can know that iterator is actually a type, not a static value.
To know this in detail, read this FAQ:
Where and why do I have to put the "template" and "typename" keywords?
Beside, you should be writing your function template as:
template <class T, class Allocator>
void function(std::list<T, Allocator> param)
{
//code..
}
Related
I write interfaces through concepts for implementation validation.
There are no problems with conventional methods:
// Interface realization
struct Realization
{
int* TestMethod(const std::string& aStr)
{
return (int *) aStr.c_str();
}
};
// Concept
template <typename T>
concept IRealization = std::is_same_v<decltype(&T::TestMethod), int* (T::*)(const std::string&)>;
// and then, for example
void Check()
{
static_assert(IRealization<Realization>)
}
but when I try to write a similar check for a template method:
// Interface realization
struct Realization
{
template <typename T>
int* TemplateMethod(const T& aStr)
{
return (int *) aStr.c_str();
}
};
, I run into a problem of dectype a template method, because I cant write
decltype(&RealizationImpl::TemplateMethod)
(at the time of checking the interface, I do not know the type that will be substituted)
Please tell me, can I somehow get the signature of the template function without type, or otherwise solve my problem? Thanks!
You should not write concepts like this. A concept should never check for something as specific as a member function with an exact signature. A concept should instead say that, given an instance of the type in question, I should be able to do i.memberFunc(...), where ... is the list of parameters.
For example, your "IRealization" concept (please don't prefix concepts with I. Concepts are not interfaces) ought to say "T must have a member function which can be called given a std::string argument and results in something which is convertible to an int." That would look like:
template <typename T>
concept IRealization = requires(T t, std::string str)
{
{ t.TestMethod(str) } -> convertible_to<int>;
};
This allows the user to provide a TestMethod that takes, for example, std::string_view instead of std::string. There's no point in being so incredibly restrictive on the type.
A concept that checks for T having a member function which is callable with some type U would have to be templated on both T and U:
template <typename T, typename U>
concept IRealization = requires(T t, U u)
{
{ t.TestMethod(u) } -> convertible_to<int>;
};
What is the problem with adding another type to the concept?
// Concept
template <typename T, typename U>
concept IRealization = std::is_same_v<decltype(&T::template TestMethod<U>), int* (T::*)(const U&)>;
For instance.
You could even make it prettier by creating a typedef -
template<typename T, typename U>
using FuncT = decltype(&T::template TestMethod<U>);
I want to define function outside the template class as described below.
Already tried a lot of combinations for the second argument which is a template and takes default argument as well.
template <typename T>
class CustomAllocator
{
//My custom allocator
};
template <typename T, typename Allocator = CustomAllocator<T> >
class CustomContainer
{
void push_back();
};
/*I want to define push_back outside my class, tried everything.
Almost 4 hours spent through stackoverflow, fluentcpp and all sites*/
// What should be specified for Allocator here ?
template <typename T>
void CustomContainer<T,Allocator>::push_back(T value)
{
}
//OR
template <typename T>
void CustomContainer<T,CustomAllocator<> >::push_back(T value)
{
}
I expect it to be defined outside class
Actual getting compiler error, if it is simple type I could easily mention int,float etc. in the second argument.
Outside of your class definition, it will be unclear to a function what type Allocator is, so you have to redeclare it just like you redeclared T
template <class T, class Allocator>
void CustomContainer<T,Allocator>::push_back(T value)
{
// ...
}
(I'm assuming that DataType should be T)
Note that your declaration of push_back, in the class should match the definition:
template <typename T, typename Allocator = CustomAllocator<T> >
class CustomContainer
{
void push_back(T);
};
You may not use default template arguments for a member function of a template defined outside the template definition.
From the C++ 17 Standard (17.1 Template parameters)
... A default template-argument shall not be specified in the template-
parameter-lists of the definition of a member of a class
template that appears outside of the member’s class.
So just write
template <typename T, typename Allocator>
void CustomContainer<T, Allocator>::push_back( const T &value )
{
//...
}
Pay attention to the argument of the function. Your declaration of the function does not correspond to its definition.
I have class World which manages creation of object... After creation it calls afterCreation method and I the created object is user-defined type derived from Entity (eg. MyEntity), I want to call addEntity. I the object was something else, I want to do nothing.
addEntity must be called with appropriate T, because it generates unique IDs for every derived class etc.
Here is my solution:
template <int v>
struct ToType
{
enum { value = v };
};
template <typename T>
void World::afterCreation(T * t)
{
afterCreation(t, ToType<std::is_base_of<Entity, T>::value>());
}
template <typename T>
void World::afterCreation(T * t, ToType<true>)
{
addEntity(t); //here I cant pass Entity *, I need the real type, eg. MyEntity
}
template <typename T>
void World::afterCreation(T * t, ToType<false>)
{
}
My question is - Can in be done better way?
How can I simulate following code without ToType or similar?
template <typename T>
void afterCreation(){/*generic impl*/}
template <typename T where T is derived from Entity>
void afterCreation(){/*some specific stuff*/}
"specialize" in the title is only to describe my intention, no need to solve problem with template specialization
It is not going to make it much better, but you can remove one level of indirection by using SFINAE:
template <typename T>
typename std::enable_if< std::is_base_of<Entity, T>::value >::type
World::afterCreation(T * t)
{
// Derived from Entity
}
template <typename T>
typename std::enable_if< !std::is_base_of<Entity, T>::value >::type
World::afterCreation(T * t)
{
// generic
}
How does this work? When the compiler finds the call to afterCreation it tries to determine which of the overloads is best, and for that it matches the types and tries to perform the substitution. In both cases, the matched type (from the arguments) and apply the substitution to the whole expression. The enable_if template contains an inner type type if the value passed as the first argument is true or else it does not contain such type. During the substitution of the types, one of the overloads will yield an invalid function signature (the one for which the condition is false) and will be dropped from the set of candidates.
You can do this with polymorphic pointers:
template <typename T>
void afterCreation(T* x) {
T* entity = dynamic_cast<Entity*> x;
if (!entity) {
// ... generic implementation
} else {
// ... entity implementation, use "entity"
}
}
Though this might not be the best solution since this has a (tiny) run-time overhead. A very smart compiler might remove this overhead through static analysis but I doubt compilers will pick this up.
Why can't the compiler figure out these template parameters? Is there a way to make it do so?
(I'm using Visual Studio 2010.)
template<typename T, typename TFunc>
void call(TFunc func) { func(T()); }
void myfunc(void *) { }
int main() { call(myfunc); }
T appears nowhere in the parameter list so T cannot be deduced from the function arguments. All types to be deduced must appear in deduced contexts in the parameter list. For example,
template <typename TReturn, typename TParameter>
void call(TReturn (*f)(TParameter))
{
f(TParameter());
}
Template parameter deduction for function templates only works based on function arguments, nothing else. The function definition is never looked at for the purpose of determining the template parameters, so your parameter T cannot possibly be deduced.
You could remedy your situation by incorporating the type into the function signature: Since you expect the outer function to be called with a function itself, make that explicit:
template <typename T> void foo(void(*f)(T))
{
T x;
f(x);
// ...
}
Combine function overloading with functors, and it becomes impossible in the general case to determine what arguments can be passed to a callable entity.
Consider, for example
struct FunctorExample {
void operator()(int x) {...}
std::string operator()(const std::string& ) {...}
};
If there were some way to coax the compiler to pattern match on arguments, it would have to have undefined or error behavior when applied to FunctorExample.
Instead, the trend seems to be that when you want to template metaprogram with functors, you specify the functor and argument list. Examples (off the top of my head) being boost::result_of and boost::fusion.
Edit: That said, if you're willing to restrict your attention somewhat, and you can use some C++11 syntax (decltype), you can arrange to introspect a bit more:
// Support functors with a very simple operator():
template <typename T> struct argument :
public argument<decltype(&T::operator())> {};
// Pointers to member functions
template <typename C, typename R, typename A> struct argument<R(C::*)(A)>
{typedef A type;};
// Function types
template <typename R, typename A> struct argument<R(A)> {typedef A type;};
// Function pointer types.
template <typename R, typename A> struct argument<R(*)(A)> {typedef A type;};
// Now for call:
template <typename FuncType>
void call(FuncType func) {
typedef typename argument<FuncType>::type Arg;
func(Arg());
}
// example:
class FunctorInt {public: int operator()(int ) {return 0;};};
void myfunc(void *) {}
int main() {
call(myfunc);
call(FunctorInt());
}
Variadic templates could be used to expand this stuff to support more than one argument.
Consider the following code:
template <typename Datatype>
class MyClass
{
void doStuff();
template <typename AnotherDatatype>
void doTemplateStuff(AnotherDatatype Argument);
};
template <typename Datatype>
void MyClass<Datatype>::doStuff()
{
// ...
}
template <typename Datatype>
template <typename AnotherDatatype>
void MyClass<Datatype>::doTemplateStuff(AnotherDatatype Argument)
{
// ...
}
The implementation for the second member function, doTemplateStuff, will not compile if I condense it like this:
template <typename Datatype, typename AnotherDatatype>
void MyClass<Datatype>::doTemplateStuff(AnotherDatatype Argument)
{
// ...
}
Why is this? Shouldn't separating template information by commas have the same effect as putting each typename on its own line? Or is there some subtle difference I'm not aware of...?
(Also, if someone can think of a better title please let me know.)
This is an excellent question. I don't know the specific reason that the standards committee decided to design templates this way, but I think it's a callback to lambda calculus and type theory. Mathematically speaking, there is an isomorphism between any function that takes two arguments and returns a value and a function that takes in a single argument, then returns a function that takes in yet another argument and then returns a value. For example:
λx. λy. x + y
is isomorphic with (but not identical to)
λ(x, y). x + y
where (x, y) is a single object representing the pair of x and y.
With C++ member function templates, C++ chose to use the first of these systems. You have to specify all the arguments for the outermost function, then, separately, all of the arguments for the innermost function. Mathematically this is equivalent to specifying all of the arguments at the same time in one argument list, but C++ didn't choose to do this.
Now, a really good question is why they didn't do this. I'm not fully sure of the rationale, but if I had to guess it's because of weird interactions with template specialization. If I can think of something specific I'll update this post.
Putting comma's between the template declaration tells the compiler to expect two template parameters. In your case, because the object is a template object when you declare the function as you do you're violating your own declaration. It's looking for that second template in the MyClass object, referencing the actual class declaration and realizing that it's an error.
Hence,
template<typename T, typename V>
struct Foo{
void bar();
};
template<typename T, typename V>
void Foo<T,V>::bar(){...}
is what it's expecting to see.
template<typename T>
struct Foo{
void bar();
}
template<typename T, typename V>
void Foo<T>::bar(){...}
is an error. It's wondering where that other template parameter came from.
If you want to do this you'll need to write the function right there:
template<typename T>
struct Foo{
template<typename V>
void bar(const V& _anInputValue){
cout << _anInputValue;
}
void baz();
};
template<typename T>
void Foo<T>::baz(){
cout << "Another function.";
}