I have a function taking, as an argument, a template class with multiple parameters (see 'func2' in the following code). I'd like the function to be able to take as argument a class inheriting from the agument type, and automatically resolve template types by upcasting. In the following example, it's ok for function 'func1', but it's apparently not possible straightforwardly when argument is a multiple template class as for function 'func2'.
The error message states: "Candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding template template parameter"
I understand (and kind of agree) with the message. But this is the same case for 'func1', and it's working fine.
So my question is, is there a way I can treat variable 'd' as type 'C' by automatic upcasting when using a function ? And if so, how ?
I'm using Xcode 5.1.1, clang-503.0.40. and C++11 option. Thanks
template <class T1>
class A {};
class B : public A<int> {};
template <template <class T1> class T, class T1 >
void func1(T<T1> _arg) {}
template <class T1, class T2>
class C {};
template <class T1>
class D : public C<T1,int> {};
template <template <class T1, class T2> class T, class T1, class T2>
void func2(T<T1,T2> _arg) {}
int main() {
A<int> a;
B b;
func1(a);//works
func1(b);//works, T1 is resolved
C<float,int> c;
D<float> d;
func2(c);//works
func2(d);//doesn't work,compilation error message: "Candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding template template parameter"
return 0;
}
No, there's no way to do what you're trying to do.
You can use an alias though, assuming your compiler supports them:
template < typename T >
using D = C<T,int>;
I believe that should match your function template just fine.
If you're trying to have D have different behavior then you'd create a partial specialization of C.
Ok so the best option I found is as follow. The idea is to make 'D' believe it's a multiple template class, but in fact one of the template parameters is only meant to be a specific type (int). This way, 'D2' is sort of both a one template class and two template class. There's still one partial specialization when defining template class D , but it's actually only the normal definition of class D.
template <class T1, class T2>
class C {};
template <class T1, class T2=int>
class D;
template <class T1>
class D<T1,int> : public C<T1,int> {};
template <typename T1>
using D2 = D<T1>;
template <template <class T1, class T2> class T, class T1, class T2>
void func2(T<T1,T2> _arg) {}
int main()
{
C<float,int> c;
D2<float> d;
func2(c);//works
func2(d);//works
//instantiation tests:
D<float> test1;//works -> normal
D<float, int> test2;//works -> not ideal but well ok
D<float, float> test3;//doesn't work -> also normal. States: "Implicit instantiation of undefined template 'D<float, float>' "
return 0;
}
Related
I would like to define a derived class that has a template template type for a templated base class. Is this possible, and if so what is the syntax? I have tried a lot of things without success. Please consider this code:
#include <iostream>
template <typename U>
struct Wrapper {};
template <typename U>
struct Base {};
// This Works, define a template template type class
template<class>
struct Other;
template<template<typename T> typename W, typename T>
struct Other<W<T>> {};
// How do I define this?
template<template<typename T> typename W, typename T>
struct Derived : public Base<W<T>> {};
using namespace std;
int main()
{
Wrapper<float> w;
Base<int> b;
Other<Wrapper<int>> o;
Derived<Wrapper<int>> d; // fails to compile
return 0;
}
Why: I have existing/working code that has Derived<U> where U is a wrapped class W<T>. Specifying Derived<W<T>> in the definition using a template template type was unnecessary; Derived<U> is sufficient. Both W and T can be many different concrete types, but T will always have a statically available method that I now need access to from within Derived scope.
I am currently and woefully limited to C++11.
Often, things get much simpler if you realize that, for most regards, an instantiation of a class template is just a type like any other. And confusing types with templates is also the source of your error.
Wrapper<int> is a type. The first argument for the Derived template is a template, not a type.
Other is a template that has a single type argument. It has a partial specialization for the case that this type is an instantiation of some template W.
Derived is a template that has two arguments. The first is a template argument, the second is a type. An instantiation is, for example: Derived<W,int>.
If you want to make Derived a template with a single type argument, with a partial specialization for the case that this type is an instantiation of Wrapper, then you need to do the same as you did for Other: A base template with a single type argument and a specialization, eg:
template<class> struct Derived;
template<template<typename T> typename W, typename T>
struct Derived<W<T>> : public Base<W<T>> {};
The second line is the partial specialization. It does not change the fact that Derived has one type argument. It only makes Derived<W<T>> match this specialization no matter what T is.
What I want is something like std::bind for functions but for templates.
Assume I have a template which needs a template template with a defined set of arguments.
Now I have another template which would work but which has more arguments, so I need to convert this complex template into a simpler one with bound arguments.
To do this I created a template which defines an alias template.
If I use this binding template with concrete types this works well. But if I instantiate the binding template with another template argument, gcc and clang assume that the alias is not a template template. I know it becomes a dependent name, but there is nothing like the typename disambiguator for templates.
With icc and msvc this works fine.
template<
template<typename> typename Template
>
class ATypeWhichNeedsATemplateTemplate
{
};
template<typename A, typename B>
class AComplexTemplate
{
};
template<
template<typename...> typename ATemplate
,typename... Args
>
class Binder {
public:
template<typename T>
using type = ATemplate<T, Args...>;
};
template<typename T>
class AClassWithBoundTemplate : public ATypeWhichNeedsATemplateTemplate<
Binder<AComplexTemplate, T>::type
>
{
};
see on godbolt
clang complains:
<source>:30:5: error: template argument for template template parameter must be a class template or type alias template
Binder<AComplexTemplate, T>::type
^
gcc says something similar:
<source>:31:1: error: type/value mismatch at argument 1 in template parameter list for 'template<template<class> class Template> class ATypeWhichNeedsATemplateTemplate'
>
^
<source>:31:1: note: expected a class template, got 'Binder<AComplexTemplate, T>::type'
type is a dependent template name. You need to spell it like Binder<AComplexTemplate, T>::template type, i. e.:
template<typename T>
class AClassWithBoundTemplate : public ATypeWhichNeedsATemplateTemplate<
Binder<AComplexTemplate, T>::template type
>
{
};
Fixed godbolt example: https://godbolt.org/z/7nJtAU
I can use std::experimental::is_detected to check whether chips1_t can be instantiated with float* (which it can't) as shown in the static_assert below:
#include <experimental/type_traits>
#include <type_traits>
template <typename, typename = void>
struct chips1;
template <typename T>
struct chips1<T*,std::enable_if_t<std::is_same<T,int>::value>> {
using type = int;
};
template <typename T> using chips1_t = typename chips1<T>::type;
static_assert(!std::experimental::is_detected<chips1_t,float*>::value,"");
If I then try a check using chips2_t, a similar static_assert, shown below, will produce a compilation error; regarding the missing type member. Can anyone tell me why?
struct Base {};
template <typename>
struct chips2;
template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
using type = int;
};
template <typename T> using chips2_t = typename chips2<T>::type;
static_assert(!std::experimental::is_detected<chips2_t,float*>::value,"");
In the second case, the base class is not part of the template type, so it's not considered during SFINAE (It's a dependent base class)
Instantiating
template <typename T>
struct chips2<T*>
Succeeds, and then compilation fails because it derives from std::enable_if_t<std::is_same<T,int>::value,Base> which becomes an ill-formed expression.
You cannot specialize templates by their base class*.
For example, you couldn't have two competing specializations like:
template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
using type = int;
};
template <typename T>
struct chips2<T*> : std::enable_if_t<!std::is_same<T,int>::value,Base> {
using type = float;
};
They would be considered identical from a specialization standpoint (i.e., they are identical from a naming standpoint, so the compiler will consider the second one to be a redeclaration of the first one). The only part that matters is everything up to the colon :
In the first case, your enable_if is directly part of the template specialization, so SFINAE works properly.
Another note
In the second one, you've effectively made it impossible to instantiate chips2 with anything other than int*, see the following example:
struct Base {};
template <typename>
struct chips2
{};
template <typename T>
struct chips2<T*>
: std::enable_if_t<std::is_same<T,int>::value, Base>
{};
int main(){
chips2<float*> c;
}
You might be inclined to think that SFINAE will choose the base class template for c, but in reality it chooses the specialization chips2<T*> because of what I said above, and compilation fails.
*Relevant standardese at [temp.spec]
the name of the class that is explicitly specialized shall be a simple-template-id.
Where a simple-template-id is of the form:
template-name < template-argument-listopt >
e.g. chips2<T*>. Note that there is no option to also include the class it derives from
template <class T1, class T2>
class A {};
template <class T1>
class A {};
A<int, int> a;
A<int> b;
This code generates
error C2976: 'A' : too few template arguments
at the second diclaration of 'A' class.
Your first declaration defines a class A with 2 template arguments. Anything after that must either be a specialization of that or some other enabled version.
If you want to allow either 1 or 2 template arguments you can use variadic templates as follows:
template <class... Args>
class A;
template <class T1, class T2>
class A<T1, T2> {};
template <class T1>
class A<T1> {};
Live demo
There is no SFINAE going on here. You are redeclaring the class A, originally with 2 template parameters, to another one with only 1 template parameters, and hence the error. g++ gives a more explicit error:
error: redeclared with 1 template parameter class A {};
note: previous declaration 'template class A' used 2 template parameters
SFINAE is about a substitution failure; i.e. the problem must be a consequence of what types you are placing as template parameters. Not every error qualifies... as an extreme example consider
template<T>
struct foo {
!##*&!%^#
};
this is also an error, but not a substitution failure :-)
I'm trying to declare class B such as written below. The problem is, as far as I know, A<B, T1> is actually implicitly considered as A<B<T1>, T1> which doesn't match class A definition. Therefore, my question is: is it possible to use class B inside its own definition, in its "template-non-instanciated" form? And if so how?
template <template <class T1> class T2, class T1>
class A {
};
template <class T1>
class B {
A<B, T1>* example;
};
which results in the following compilation error with gcc4.2 :
error: type/value mismatch at argument 1 in template parameter list for 'template < template < class T1 > class T2, class T1 > class A'
error: expected a class template, got 'B< T1 >'
This seems to be a bug in g++-4.2 as I can reproduce your problem with that compiler.
If you can move to at least 4.4 your code will compile as-is. Alternately you can write A< ::B, T1>* example; to force it to use the global scope template, which compiles in 4.2.