I have a class that needs to use some sort of map. By default, I want to use std::map, but I also want to give the user the ability to use something different if they want (e.g. std::unordered_map or maybe even a user created one).
So I have code that looks like
#include <map>
template<class Key, template<class, class> class Map = std::map>
class MyClass {
};
int main() {
MyClass<int> mc;
}
But then, g++ complains
test.cpp:3:61: error: template template argument has different template parameters than its corresponding template template parameter
template<class Key, template<class, class> class Map = std::map>
^
test.cpp:8:14: note: while checking a default template argument used here
MyClass<int> mc;
~~~~~~~~~~~^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/map:781:1: note: too many template parameters in template template argument
template <class _Key, class _Tp, class _Compare = less<_Key>,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:3:21: note: previous template template parameter is here
template<class Key, template<class, class> class Map = std::map>
^~~~~~~~~~~~~~~~~~~~~~
1 error generated.
So it looks like g++ is unhappy that std::map has default arguments.
Is there a way I can allow Map to be any sort of template that can accept at least two template arguments?
I would prefer to stick with C++98 if I can, but I'm open to C++11.
The problem is that your template template parameter has only two template parameters, as opposed to map, which has four.
template<class Key, template<class, class, class, class> class Map = std::map>
class MyClass {
};
Or
template<class Key, template<class...> class Map = std::map>
class MyClass {
};
Should compile. However, to avoid such problems, try to take the map type instead, and extract the key type via the corresponding member typedef. E.g.
template <class Map>
class MyClass {
using key_type = typename Map::key_type;
};
Your code will compile in C++17. A long-standing defect report of the C++ Core Working Group (CWG 150) was resolved (by P0522R0) in time for C++17.
cppreference.com also discuss this here, and include a helpful example:
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template <class ...Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK in C++17 after CWG 150
// Error earlier: not an exact match
X<C> xc; // OK in C++17 after CWG 150
// Error earlier: not an exact match
Testing with my version of GCC (8.3.0), I find that using the -std=c++17 flag will successfully compile your program; while using earlier versions of C++ (e.g. -std=c++14 or -std=c++11) will fail.
Related
I am trying to use a member type of a template class, which does not depend on any template parameters of the template class. I would like to keep the type as the member type due to its logic, but I do not want to specify the unnecessary template parameters of the class whenever I want to use the member type outside the class.
Consider the following:
class Dummy { };
// Template class
template<typename T>
class A {
public:
template<typename T2>
class MemberType : public T2 {
public:
T2 t2;
};
};
int main()
{
typename A<Dummy>::template MemberType<Dummy> m1; // okay
typename A::template MemberType<Dummy> m2; // not okay!
return 0;
}
I got the following compiler error when I try to compile using g++:
error: ‘template<class T> class A’ used without template parameters
typename A::template MemberType<Dummy> m2; // not okay!
Is there any workaround for this?
I am trying to use a member type of a template class, which does not
depend on any template parameters of the template class.
As a nested type within class A<T>, MemberType does depend on the template parameter T.
i.e. A<T>::MemberType<T2> and A<U>::MemberType<T2> are distinct classes.
What you want to do is not possible. A template is just a template. There is very little you can do with it before actually instantiating it for a concrete type. There could be a specialization for A that has no nested MemberType at all.
I would like to keep the type as the member type due to its logic,
[...]
...but it seems the logic is something else: The MemberType does not depend on A, hence it should not be part of a template that depends on A.
Sloppy speaking template<typename T> can be read as "everything that follows depends on T". Even if you think it does not, there could always be a specialization that changes anything inside A. If you want MemberType to not depend on T then declare it outside A.
Everything in a template is dependent on the parameter(s) - meaning a template-specialization might not even have class MemberType.
But you can make a default parameter - you still need to write <> though (but you can omit template usually - even typename, but I left that):
class Dummy { };
// Template class
template <class T = void>
class A {
public:
template<typename T2>
class MemberType : public T2 {
public:
T2 t2;
};
};
int main()
{
typename A<Dummy>::MemberType<Dummy> m1; // okay
typename A<>::MemberType<Dummy> m2; // also ok
return 0;
}
As others have pointed out, this somewhat looks like an anti-pattern though - since the inner type is not dependent on the parameter of the outer template class.
Is there any workaround for this?
MemberType is a type dependent from a template parameter so, necessarily, you have to pass through the containing template a template parameter to define it
typename A<SomeType>::template MemberType<AnotherType> m2;
Taking in count that your not interested in external SomeType parameter, the best workaround I can imagine is the use of a using as follows (or something similar)
template <typename T>
using MemberType_t = typename A<T>::template MemberType<T>;
to reduce typewriting.
The following is a full compiling simplified example
#include <type_traits>
class Dummy { };
template <typename>
struct A
{
template <typename T2>
struct MemberType : public T2
{ T2 t2; };
};
template <typename T>
using MemberType_t = typename A<T>::template MemberType<T>;
int main ()
{
typename A<Dummy>::template MemberType<Dummy> m1;
MemberType_t<Dummy> m2; // compile
static_assert( std::is_same<decltype(m1), decltype(m2)>::value, "!" );
}
I want to create a class that accepts only certain types of template classes. I know that there exists template specialization, but I want my class to accept all templates that implement a specific function, search.
Let's say I have a class A as follows:
template<class T> //add a restriction that T implements bool search(T)
class A
{
T t;
//do something that uses T.search(T x)
if(t.search(x))
//Do something
};
So basically, I want to create a generic class that works for all classes that have the search functionality. Is there a way to do this?
I want to create a generic class that works for all classes that have the search functionality. Is there a way to do this?
By example, using decltype() as follows
template <typename T,
typename = decltype(std::declval<T>().search(std::declval<T>()))>
class A
{
};
The following is a full compiling example for a size() enabled class A
#include <string>
#include <type_traits>
template <typename T, typename = decltype(std::declval<T>().size())>
class A
{
};
int main()
{
A<std::string> as;
//A<int> ai; // compilation error
}
There is a drawback in this solution: you can hijack it explicating the second template parameter; by example, the following code compile
A<int, void> ai;
To avoid this problem you can use partial specialization as follows
template <typename T,
typename = decltype(std::declval<T>().search(std::declval<T>()))>
class A;
template <typename T>
class A<T>
{
};
Consider the following:
template <class...>
struct MyT;
template <class T>
struct MyT<T> {};
template <template <class> class TT = MyT> struct A {}; // fine
using B = A<MyT>; // does not compile
int main() {
return 0;
}
When MyT is used as a default argument of A, the compiler (g++ 5.4.0) is happy. However, when it is used to instantiate A, the story is different:
temp.cpp:19:16: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class> class TT> struct A’
using B = A<MyT>;
^
temp.cpp:19:16: note: expected a template of type ‘template<class> class TT’, got ‘template<class ...> struct MyT’
I can fix it by introducing an alias:
template <class T>
using MyTT = MyT<T>;
using B = A<MyTT>; // fine
The question: what is the reason for the error and is there a solution without introducing an alias?
EDIT Please note that A is declared to have a template template parameter as shown and that is not given for change.
You cannot do that and you cannot use such a type as a default parameter. The fact that it seems to be accepted as long as you don't rely on it doesn't mean that the default parameter is a valid one.
Consider the following code that explicitly uses the default type:
template <class...>
struct MyT;
template <class T>
struct MyT<T> {};
template <template <class> class TT = MyT> struct A {}; // fine
int main() {
A<> a;
return 0;
}
The error is quite clear:
template template argument has different template parameters than its corresponding template template parameter
Partial specializations are not taken in account in this case, thus the two declarations differ.
You should either declare A as:
template <template <class...> class TT = MyT> struct A;
Or declare somewhere a type that is constrained to a single argument, as an example by means of an using declaration as you did.
First, the default argument doesn't work either.
Second, template template arguements are a strange beast. It would make sense if a template template argument would take anything that could be instantiated with the signature described in the template template argument.
That is not how it works.
Instead it works the other way around.
template<template<class...>class Z> struct foo {};
template<template<class >class Z> struct bar {};
template<class...>struct a{};
template<class >struct b{};
foo will accept a or b.
bar will accept only b.
The correct response to this, once you understand it, is "what the hell?". If you aren't responding "what the hell" back up and see if you can understand it. This basically works backwards from typical typing for arguements in C++; it behaves more like a return type than an argument. (Learn the terms contravariance and covariance if you want to see some of the language that lets you talk about this directly)
This is quite non-intuitive, and why it works this way exactly would involve tracking down the pre-history of C++.
But, as a benefit, a template<class...>class argument is in effect an "any template that only takes type parameters". I find this highly useful.
As a downside, template<class>class arguements are almost completely useless.
Tl;dr: make your template<template parameters be template<template<class...>class, and metaprogram only with templates that only take types. If you have a template that takes values, write a type wrapper that replaces a requirement for a std::size_t X with a std::integral_constant< std::size_t, X >.
Forgetting for a moment the question of "Why would you do this?",
the first version would work if you hadn't done any template specialization.
template <class T>
struct MyT { };
template <template <class> class TT = MyT> struct A
{};
using B = A<MyT>;
With template specialization, the compiler must determine the best match, but since you haven't ever actually provided any template arguments it's ambiguous.
When you introduce MyTT you are using a single template argument, and the compiler is smart enough to see that you have a specialization when there is only one arg:
template <class T>
using MyTT = MyT<T>;
It chooses the specialization instead of the variadic version in this case.
But now we circle back to the grand question... why? Unless within A you're always instantiating MyT with a specific class, it's pointless to use A at all:
template<template<class> class TT = MyT> struct A
{
// use an instance of TT??
TT<int> myInstance; // forced to choose `int`
};
I would like to split your question into 2 parts.
A) Consider the template of your structure is simpler
template <class T>
struct TestStruct {
};
template <
template <class>
class TT = TestStruct
>
struct A
{
int a; // sorry to modify this. This help to show how it works
};
int main() {
A< TestStruct > b;
b.a; // it works!
return 0;
}
It works because of the template class TT only accept the template with < class... > template. The specializated class is not count on this ( because the underlying of it is still template < class ... > )
B) even you update your struct A to the template< class... > one, you still have one more problem. What is the template argument of TT? Please see the example below
template <class...>
struct MyT;
template <class T>
struct MyT<T> {
int a;
};
template <
template <class...>
class TT = MyT
// may be you need to add some more typename here, such as
// typename T1, ... , and then TT<T1> a;
>
struct A
{
TT<int> a;
// Here ! TT is a template only, do not have the template parameters!!!
};
int main() {
A< MyT > b;
b.a; // it works!!
return 0;
}
But, if you really cannot update the signature of those definitions, you can do a proxy class
template< class T >
struct Proxy : MyT<T>
{
};
template <typename _Type, typename _Comparator=less<typename _Type::value_type> >
class Myclass
// and then comes the rest of my class
I want to create a specialized template with strings (but also working with other comparator than the default one)
I tried this but it is not compiling.
template <typename _Comparator=less<typename _Type::value_type> >
class Myclass<string>
I know that the next example works but it uses the default comparator I fixed.
template <>
class Myclass<string>
It is not allowed to specify default template arguments in the class template specialization. Use the following syntax to achieve what you want:
template <typename Comparator>
class Myclass<string, Comparator>
{
// ...
};
You can make another default template parameter:
template<class Comparator = other_functor>
class Myclass<string, Comparator> {
};
Remove the =less<typename _Type::value_type> when you declare the specialization.
I need to use a template class which is defined in another template class as parameter of another template as return value in template method. I know it sounds complicated, code below explains it better. Problem is that the code cannot be compiled, it ends with following error:
type/value mismatch at argument 2 in template parameter list for 'template<class T, template<class> class Policy> class Result'
expected a class template, got 'CDummy<T2>::Policy2'
but I'm pretty sure that given class fulfills needs. Problem is that the method, which uses it, is template too and so compiler does not know what exactly CDummy<T2>::Policy2 is. If the Policy2 would not be template, but regular class or if I could fill its argument, I would use typename which would tell the compiler not to worry about it, but how can this be done with template?
// I cannot change this interface - it's given by a library
template <class T, template <class> class Policy>
class Result : public Policy<T>
{
T data;
};
template <class T>
class Policy1
{
};
// I use this for allowing Policy2 to change behaviour according Dummy
// while it keeps template interface for class above
template <class Dummy>
class CDummy
{
public:
template <class T>
class Policy2 : public Policy1<T>
{
};
};
// Both variables are created ok
Result<int, Policy1 > var1;
Result<int, CDummy<float>::Policy2 > var2;
// This is ok, too
template <class T>
Result<T, Policy1 > calc1()
{
return Result<int, Policy1>();
}
// But this ends with the error:
// type/value mismatch at argument 2 in template parameter list for 'template<class T, template<class> class Policy> class Result'
// expected a class template, got 'CDummy<T2>::Policy2'
template <class T1, class T2>
Result<T1, CDummy<T2>::Policy2 > calc2() // <-- Here is the generated error
{
typedef typename DummyTypedef CDummy<T2>;
return Result<T1, DummyTypedef::Policy2>();
}
Notes:
I use gcc 4.7.3 32bit in GNU/Linux Ubuntu 13.04. 32 bit.
For various reasons, I cannot use C++11 standard (yet) and so I cannot use template typedefs
I believe that the name CDummy<T2>::Policy2 is a dependent name in that context and that you should use the template keyword to inform the compiler that it is indeed a template.
template <class T1, class T2>
Result<T1, CDummy<T2>::template Policy2 > calc2() // <-- Here is the generated error
// ^^^^^^^^
additionally the implementation of that same function seems to be wrong also. The order of typedefs is original name, new name, and CDummy<T2> is known to be a type (i.e. there is no need for the typename):
typedef CDummy<T2> DummyTypedef;
The return statement would then be:
return Result<T1, DummyTypedef::template Policy2>();