Using a specialization of variadic template as a template argument - c++

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

Related

Template type deduction in container initialisation

I understand that a template type parameter can be defaulted, however, this doesn't work when I attempt to construct a std::vector without supplying the template argument:
#include<vector>
template <typename Int = int>
struct integer{
Int i;
};
int main(){
integer num;
std::vector<integer> vec;
return 0;
}
This code returns a type value mismatch (compiler explorer link). Is it possible to fix this without writing std::vector<integer<int>>
If you have a function with a default parameter:
int foo(int bar=0);
To call this function you still have to write:
int n=foo();
attempting to write int n=foo; will not work, of course. For the same reason if your template's parameters are defaulted you still have to use
integer<>
to instantiate the template instead of
integer
You could create an alias integer_t for interger<> using using.
template <typename Int = int>
struct integer{
Int i;
};
using integer_t = integer<>;
int main(){
std::vector<integer_t> vec; // No error.
}
The reason why std::vector<integer> did not work is explained in this answer.
You can provide a wrapper that accepts template template parameter (read below):
template <template <typename> class TT,
typename T = int>
using my_vector = std::vector<TT<T>>;
my_vector<integer> vec{}; // works
my_vector<integer, long> vecLong{}; // using non-default type
Templates have different types of template parameters:
Each parameter in a parameter-list may be:
a non-type template parameter;
a type template parameter;
a template template parameter.
template <typename>
struct with_type_parameter{};
template <template <typename> class>
struct with_template_template_parameter{};
So, there is an explicit difference for compiler between your integer<> and integer.
The first one is a type explicitly instantiated from a template parameter.
The second one is a template template.
So, when you instantiate a class template std::vector with a template template integer instead of a type integer<> for an argument, you get the error you have. Because these two things are different for class templates.
However, there is a different behavior fo function templates, since they are allowed for overloading.
Here is an example for you: https://godbolt.org/z/cbnss17aj
template <typename>
struct with_typename{};
// can't have that. class template can only be specialized when declared
// template <template <typename> class>
// struct with_typename{};
#include <iostream>
template <typename>
void with_typename_fn()
{
std::cout << "type" << std::endl;
}
// perfectly fine, since function template overloading is allowed in C++
template <template <typename> class>
void with_typename_fn()
{
std::cout << "template template" << std::endl;
}
template <typename>
struct non_defaulted{};
template <typename = int>
struct defaulted{};
int main()
{
with_typename_fn<int>(); // ok
with_typename_fn<non_defaulted>(); // also ok
with_typename_fn<defaulted>(); // also ok, but will resolve to template template specialization!
}
Output:
type
template template
template template

Passing a template's template member type as a template template argument

A class WithTTMember has a template member type named TT.
struct WithTTMember {
template<typename> using TT = void;
};
Another class ExpectTT takes a template template parameter:
template< template<typename> typename TT >
struct ExpectTT {};
ExpectTT<WithTTMember::TT> can be successfully instantiated.
A third class ExpectTWithTT expects a template parameter with a template member type named TT, and instantiates ExpectTT using it:
template<typename T>
struct ExpectTWithTT {
using X = ExpectTT<typename T::TT>; // this doesn't compile
};
I expect ExpectTWithTT<WithTTMember>::X to be the same type as ExpectTT<WithTTMember::TT>. However the code above is fails to compile.
I tried injecting the faulty line with a combination of template and typename keywords following compiler messages and my instinct, but I couldn't get it to work.
How can I express what I want?
Any C++ version is fine.
You should use template keyword to tell that T::TT is a template.
template<typename T>
struct ExpectTWithTT {
using X = ExpectTT<T::template TT>;
// ^^^^^^^^
};

typedef X<T>=T::UserType1, but if not applicable, typedef X<T>=UserType2

Here is MCVE (uncompilable) :-
#include <iostream>
#include <type_traits>
//-- library ---
template<class T,template<class>class Slot,class DefaultType>
class GetType{
template <typename C> static Slot<T> check( Slot<T>*);
template <typename> static DefaultType check(...);
public: using type=decltype(check<T>());
};
template<class T,template<class>class Slot,class DefaultType>
using X = typename GetType<T,Slot,DefaultType>::type;
Here is its usage :-
//--- user defined ---
class B {public: using MyType=int;};
class C{};
template<class T> using SlotCustom = typename T::MyType;
int main(){
using ShouldInt=X< B ,SlotCustom ,long>; //B::Mytype =int , result:int
using ShouldLong=X< C ,SlotCustom ,long>;//C::Mytype not exist, result:long
std::cout<< std::is_same_v<ShouldInt, int> <<std::cout; //should true
std::cout<< std::is_same_v<ShouldLong, long> <<std::cout; //should true
}
My objective is to create a library typedef X< Param1 ,SlotCustom ,DefaultType> that means as the following pseudo code:-
if ( SlotCustom<Param1> has meaning) return "SlotCustom<Param1>" ;
else return "DefaultType"; //i.e. by default
How to do it?
Here is a similar question.
The main difference is that X<T> there can be only a bool, and many things are hardcoded.
I am new to template specialization. The solution might be obvious, but I can't find it.
If I understand your question correctly, then your approach can be made to work, for example
template <template <class> class Slot, class DefaultType>
struct GetType
{
template <typename T>
static Slot<T>&& deduce(T&&);
static DefaultType&& deduce(...);
template <typename T>
using type = std::remove_reference_t<decltype(deduce(std::declval<T>()))>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<Slot, DefaultType>::template type<T>;
live demo here
The problem with your initial attempt was that the call to your check function in the expression for decltype() needed some argument for overload resolution to take place so that the SFINAE magic can happen. My example above relies on std::declval to introduce a dummy argument of the necessary type. Also, note that my helper functions use references rather than passing the types by value directly. This is so that it also works with types that are not copyable. Note that there will be problems if Slot<T> or the DefaultType are reference types themselves. One would have to, e.g., introduce additional wrapper types to deal with that…
Alternatively, you could use partial class template specialization to pick the correct type, for example:
template <class T, template <class> class Slot, class DefaultType, typename = void>
struct GetType
{
using type = DefaultType;
};
template <class T, template <class> class Slot, class DefaultType>
struct GetType<T, Slot, DefaultType, std::void_t<Slot<T>>>
{
using type = Slot<T>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<T, Slot, DefaultType>::type;
live demo here
The trick here lies in the use of the last template parameter with default argument void. Due to the way the matching of partial class template specializations works (see, e.g., this answer), the specialization will only be picked if Slot<T> is a valid type. Note that above solution requires C++17. If you have to stay within C++14 (which you probably don't, given that your own example relies on C++17), you can, e.g., provide your own implementation of void_t (as explained here):
template <typename... T> struct make_void { using type = void; };
template <typename... T> using void_t = typename make_void<T...>::type;

template< template <typename> something_else >, what is this?

I'm just starting learning C++11 and I never saw this syntax in the list of new features:
template <template <typename> class F>
struct fun;
what is it and how does it work?
Note: What you are looking at is an "old" feature, and has been there since long before c++11.
template <template <typename> class F> struct Obj;
In the above Obj is a template only accepting a template-parameter which is also a template[1]; this is most often referred to as a template-template parameter [2].
1) in this specific example it will only accept a template that takes one type-parameter.2) Link to SO question: Template Template Parameters
Imagine that you'd like to have a wrapper around some class template; you don't care which class template this is as long as you can specify a template argument for it.
If so, you can use a template-template parameter as in the below example:
template<template<typename T> class TemplateType>
struct Obj {
TemplateType< int> m1;
TemplateType<float> m2;
};
template<typename T>
struct SomeTemplate { /* ... */ };
Obj<SomeTemplate> foo;
In the above, foo will be a Obj<SomeTemplate> having two members:
SomeTemplate< int> m1
SomeTemplate<float> m2
This should works in C++98 as well. This is a template as an argument from a template. I mean a template class will expected as the argument for F.
Maybe this page will help you: http://www.cprogramming.com/tutorial/templates.html

Variadic templates and copy construction via assignment

Consider this minimal example:
template <typename T, typename U>
struct foo {};
template <template <typename...> class Bar>
struct converter
{
template <typename... Args>
converter(const Bar<Args...> &);
};
int main()
{
converter<foo> c(foo<int,double>()); // This works.
// converter<foo> c = foo<int,double>(); This fails
}
The commented-out line fails with both GCC 4.5 and 4.6, with a message like:
main.cpp:10:2: error: wrong number of template arguments (1, should be 2)
main.cpp:4:8: error: provided for template<class T, class U> struct foo
main.cpp: In function int main():
main.cpp:15:37: error: conversion from foo<int, double> to non-scalar type converter<foo> requested
If, instead of using variadic templates, the specific number of template parameters is used (i.e., 2 in this case) there are no errors. I'm a bit confused since I expected the two lines to be exactly equivalent: is this an expected behaviour?
Yes, this is supposed to work. It's a GCC error. GCC doesn't support C++0x variadic templates to the fullest yet (and to be fair, the specification is still constantly changing in details).
What you say "This works" is really declaring a function; it doesn't initialize an object, which was what you intended.
For what you intended, see 14.3.3p3 which describes how template<typename...> class Bar can match foo, and 14.8.2.5p9 which describes how foo<Args...> can match foo<int, double>.
template <typename T, typename U>
struct foo {};
struct converter
{
template <template <typename...> class Bar, class ...Args>
converter(const Bar<Args...> &){}
};
int main()
{
converter c1((foo<int,double>())); // This works.
converter c2 = foo<int,double>();// This works
}