Template Argument Binding - c++

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

Related

Issue with template template parameter in function template

I am trying to use a template template parameter and my code will not complile.
template<class DataType, template<class T> class Container>
void foo(Container<DataType> test)
{
}
int main()
{
std::vector<int> testVec{1,2,3,4};
foo<int, std::vector>(testVec);
}
The error message is
"No matching function for call to 'foo'.
Candidate template ignored: invalid explicitly-specified argument for template parameter 'Container'"
Can someone help me out?
Suggestion: try with
// ...............................VVVVVVVVV
template<class DataType, template<class ...> class Container>
void foo(Container<DataType> test)
{
}
The problem is that std::vector accept two type template parameters; the second one with a default value.
If you write
// ...............................VVVVVVV
template<class DataType, template<class T> class Container>
you ask for a template-template parameter that accept a single type template parameter.
So you can solve asking two type template parameters
// ...............................VVVVVVVVVVVV
template<class DataType, template<class, class> class Container>
or, to be more flexible, a variadic list of type template parameters
// ...............................VVVVVVVVV
template<class DataType, template<class ...> class Container>
As pointed by HolyBlackCat (thanks) your code (so a template-template parameter asking for a single type template parameter) should fail before C++17 but should be accepted starting from C++17 (the matching rules are changed).
Unfortunately this works with g++ but not for clang++: the clang++ developer doesn't implement, by default, this new C++17 matching rule.
But HolyBlackCat report that also clang++ accept your code adding a special parameter: -frelaxed-template-template-args.

Why are dependent template types not deducible in partial specialization?

With clang 7.0.0, compiling this code produces the errors below:
template <typename A> class Outer
{
public:
template <typename B> class Inner
{
};
};
template<typename C> struct Foo
{
};
template <typename D, typename E> struct Foo<typename Outer<D>::template Inner<E>>
{
};
 clang++-7 -pthread -std=c++11 -o main main.cpp
main.cpp:15:42: error: class template partial specialization contains template parameters that cannot be
deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
template <typename D, typename E> struct Foo<typename Outer<D>::template Inner<E>>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:15:20: note: non-deducible template parameter 'D'
template <typename D, typename E> struct Foo<typename Outer<D>::template Inner<E>>
^
main.cpp:15:32: note: non-deducible template parameter 'E'
template <typename D, typename E> struct Foo<typename Outer<D>::template Inner<E>>
^
MSVC offers a similar error.
Why aren't D and E deducible?
During template argument deduction everything on the left-hand side of the scope resolution operator :: in a template argument of the partial specialization is a non-deduced context, meaning that a template parameter appearing there will not be deduced from the corresponding argument of the specialization.
Further, if part of a qualified type name is non-deduced context, then all parameters used to specify the type are non-deduced.
So in your example neither D nor E are deduced from typename Outer<D>::template Inner<E> and since there is no other way of deducing them, deduction fails, meaning that the partial specialization is never viable.
Foo<Outer<int>::template Inner<int> > ...
C++ compilers does not deduce in their first pass, you must tell him you are acessing a context-dependant lookup symbol manually;

Template aliases conflicting types. g++ compiles successfully while clang fails

I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:
../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^
Both compiler options were set to -std=c++14
template<typename T>
struct ConcreteData
{
T data;
};
template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};
template<typename T, template<typename U> class FieldData>
struct SomeFunc
{
};
template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}
template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;
template<typename T>
using FieldData = FieldDataType<T>;
using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang
};
template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;
template<typename U>
using DataAlias = typename Traits::template FieldData<U>;
void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang
Field dummyField;
freeFunc(new Func, dummyField);
}
};
int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();
return 0;
}
According to cppreference.com:
A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.
and
Alias templates are never deduced by template argument deduction when
deducing a template template parameter.
In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?
Borrowing the minimal example of #Oktalist.
template <typename>
class T {};
template <typename _>
using U = T<_>;
template <template <typename> class X>
void f(A<X>, A<X>) {}
if you replace f by:
template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}
the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.
The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template
Using this rule and the rules for equivalence [temp.type]/1:
T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).
This is the same scenario for the alias FieldData and the class template ConcreteData.
There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.

Why is a class/struct declaration with different number of template parameters not allowed?

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 :-)

c++ function argument, automatic upcasting of multiple templated class

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;
}