Template specialization on template member of template class - c++

This is probably only a syntax problem.
So i have this template class :
template <typename String, template<class> class Allocator>
class basic_data_object
{
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
};
And another one :
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
};
Now i want to specialize the second one's T parameter with the first one's inner typedef array_container for any given type.
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::template array_container<T>>
{
};
But this specialization doesn't seem to be matched when i pass an std::vector as the last parameter.
If i create a temporary hard coded typedef:
typedef basic_data_object<std::string, std::allocator<std::string>> data_object;
And use it for the specialization, everything works :
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};
What did i miss ? :)
Alternatively what is the best (smallest / cleanest) way to make this work ?

The C++ standard says, in [temp.class.spec.match] paragraph 2:
A partial specialization matches a given actual template
argument list if the template arguments of the partial
specialization can be deduced from the actual template
argument list (14.8.2).
14.8.2 is [temp.arg.deduct] i.e. the clause describing template argument deduction for function templates.
If you modify your code to use a similar function template and attempt to call it, you will see that the arguments cannot be deduced:
template <typename String, typename T>
void deduction_test(String,
typename basic_data_object<String, std::allocator>::template array_container<T>)
{ }
int main()
{
deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
}
(I removed the Allocator parameter, since there's no way to pass a template template parameter as a function argument and in the basic_data_object type it's a non-deduced context, I don't believe it affects the result.)
Both clang and GCC say they cannot deduce T here. Therefore the partial specialization will not match the same types used as template arguments.
So I haven't really answered the question yet, only clarified that the reason is in the rules of template argument deduction, and shown an equivalence with deduction in function templates.
In 14.8.2.5 [temp.deduct.type] we get a list of non-deduced contexts that prevent deduction, and the following rule in paragraph 6:
When a type name is specified in a way that includes a non-deduced context, all of the types that comprise that type name are also non-deduced.
Since basic_data_object<String, Allocator> is in a non-deduced context (it is a nested-name-specifier, i.e. appears before ::) that means the type T is also non-deduced, which is exactly what Clang and GCC tell us.
With your temporary hardcoded typedef there is no non-deduced context, and so deduction for T succeeds using the deduction_test function template:
template <typename String, typename T>
void deduction_test(String,
typename data_object::template array_container<T>)
{ }
int main()
{
deduction_test(std::string{}, std::vector<int, std::allocator<int>>{}); // OK
}
And so, correspondingly, your class template partial specialization can be matched when it uses that type.
I don't see a way to make it work without changing the definition of get_data_object_value, but if that's an option you can remove the need to deduce the array_container type and instead use a trait to detect whether a type is the type you want, and specialize on the result of the trait:
#include <string>
#include <vector>
#include <iostream>
template <typename String, template<class> class Allocator>
class basic_data_object
{
public:
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
template<typename T>
struct is_ac : std::false_type { };
template<typename T>
struct is_ac<array_container<T>> : std::true_type { };
};
template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
struct get_data_object_value
{
};
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value<String, Allocator, T, true>
{
void f() { }
};
int main()
{
get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
obj.f();
}
This doesn't really scale if you wanted several class template partial specializations, as you would need to add several bool template parameters with default arguments.

For some reason, the problem seems to stem from the double level of templates. I'll leave you check the 3 test cases below, they are simple:
Remove the template arguments of First: works as expected
Make First a template, but the inner type a plain one: works as expected
Make both First and the inner type templates: compiles but the output is unexpected
Note: the template template parameter Allocator is useless to reproduce the issue, so I left it out.
Note: both GCC (ideone's version, 4.8.1 I believe) and Clang (Coliru version, 3.4) compile the code, and yet produce the same baffling result
From the 3 above examples, I deduce:
that this is NOT a non-deducible context issue; otherwise why would (2) work ?
that this is NOT an alias issue; otherwise why would (1) work ?
And therefore that either the problem is much hairier than the current hints would make us believe OR that both gcc and Clang have a bug.
EDIT: Thanks to Jonathan Wakely who patiently educated me enough that I could finally understand both the Standard wording related to this case and how it applied. I will now attempt to explain this (again) in my own words. Please refer to Jonathan's answer for the exact Standard quotes (it all sits in [temp.deduct.type])
When deducing template parameters (Pi), whether for functions or classes, the deduction is done independently for each and every argument.
Each argument need provide zero or one candidate Ci for each parameter; if an argument would provide more than one candidate, it provides none instead.
Thus, each argument produces a dictionary Dn: Pi -> Ci which maps a subset (possibly empty) of the template parameters to be deduced to their candidate.
The dictionaries Dn are merged together, parameter by parameter:
if only one dictionary has a candidate for a given parameter, then this parameter is accepted, with this candidate
if several dictionaries have the same candidate for a given parameter, then this parameter is accepted, with this candidate
if several dictionaries have different incompatible (*) candidates for a given parameter, then this parameter is rejected
If the final dictionary is complete (maps each and every parameter to an accepted candidate) then deduction succeeds, otherwise it fails
(*) there seems to be a possibility for finding a "common type" from the available candidates... it is of no consequence here though.
Now we can apply this to the previous examples:
1) A single template parameter T exists:
pattern matching std::vector<int> against typename First::template ArrayType<T> (which is std::vector<T>), we get D0: { T -> int }
merging the only dictionary yields { T -> int }, thus T is deduced to be int
2) A single template parameter String exists
pattern matching std::vector<int> against String, we get D0: { String -> std::vector<int> }
pattern matching std::vector<int> against typename First<String>::ArrayType we hit a non-deducible context (many values of String could fit), we get D1: {}
merging the two dictionaries yields { String -> std::vector<int> }, thus String is deduced to be std::vector<int>
3) Two template parameters String and T exist
pattern matching std::vector<char> against String, we get D0: { String -> std::vector<char> }
pattern matching std::vector<int> against typename First<String>::template ArrayType<T> we hit a non-deducible context, we get D1: {}
merging the two dictionaries yields { String -> std::vector<char> }, which is an incomplete dictionary (T is absent) deduction fails
I must admit I had not considered yet that the arguments were resolved independently from one another, and therefore than in this last case, when computing D1 the compiler could not take advantage of the fact that D0 had already deduced a value for String. Why it is done in this fashion, however, is probably a full question of its own.
Without the outer template, it works, as in it prints "Specialized":
#include <iostream>
#include <vector>
struct First {
template <typename T>
using ArrayType = std::vector<T>;
};
template <typename T>
struct Second {
void go() { std::cout << "General\n"; }
};
template <typename T>
struct Second < typename First::template ArrayType<T> > {
void go() { std::cout << "Specialized\n"; }
};
int main() {
Second < std::vector<int> > second;
second.go();
return 0;
}
Without the inner template, it works, as in it prints "Specialized":
#include <iostream>
#include <vector>
template <typename String>
struct First {
using ArrayType = std::vector<int>;
};
template <typename String, typename T>
struct Second {
void go() { std::cout << "General\n"; }
};
template <typename String>
struct Second < String, typename First<String>::ArrayType > {
void go() { std::cout << "Specialized\n"; }
};
int main() {
Second < std::vector<int>, std::vector<int> > second;
second.go();
return 0;
}
With both, it fails, as in it prints "General":
#include <iostream>
#include <vector>
template <typename String>
struct First {
template <typename T>
using ArrayType = std::vector<T>;
};
template <typename String, typename T>
struct Second {
void go() { std::cout << "General\n"; }
};
template <typename String, typename T>
struct Second < String, typename First<String>::template ArrayType<T> > {
void go() { std::cout << "Specialized\n"; }
};
int main() {
Second < std::vector<char>, std::vector<int> > second;
second.go();
return 0;
}

The answer of Jonathan Wakely gives the reason why your code does not work.
My answer shows you how to solve the problem.
In your example, the container type over which you want to specialize is defined outside of basic_data_object thus you can of course use it directly in your specialization:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,std::vector<T,A>>
{ };
This definitely conforms with the standard and works with all compilers.
In the case where the type is defined in basic_data_object, you can move it out of the class.
Example: Instead of
template<typename S, template<class> class A>
struct a_data_object
{
template<typename T>
struct a_container
{ };
};
write this:
template<typename S, template<class> class A, typename T>
// you can perhaps drop S and A if not needed...
struct a_container
{ };
template<typename S, template<class> class A, typename T>
struct a_data_object
{
// use a_container<S,A,T>
};
Now you can specialize with:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,a_container<S,A,T>>
{ };
Note: The next "solution" is apparently a bug with GCC 4.8.1.
If the container is only defined in an enclosing template and can not be moved out you can do this:
Get the container type out of basic_data_object:
template<typename S, template<class> class A, typename T>
using bdo_container = basic_data_object<S,A>::array_container<T>;
Write a specialization for this type:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,bdo_container<S,A,T>>
{ };

Alternatively what is the best (smallest / cleanest) way to make this work?
Arguably, it is:
Write a SFINAE trait template Tr<String,Allocator,T> that determines whether T is the
same as basic_data_object<String,Allocator>::array_container<T::E>
for some type E - if such there be - that is T::value_type.
Provide template get_data_object_value with a 4th parameter
defaulting to Tr<String,Allocator,T>::value
Write partial specializations of get_data_object_value instantiating that
4th parameter as true, false respectively.
Here is a demo program:
#include <type_traits>
#include <vector>
#include <iostream>
template <typename String, template<class> class Allocator>
struct basic_data_object
{
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
};
template<typename T, typename String, template<class> class Allocator>
struct is_basic_data_object_array_container
/*
A trait template that has a `static const bool` member `value` equal to
`true` if and only if parameter type `T` is a container type
with `value_type E` s.t.
`T` = `basic_data_object<String,Allocator>::array_container<T::E>`
*/
{
template<typename A>
static constexpr bool
test(std::is_same<
A,
typename basic_data_object<String,Allocator>::template
array_container<typename A::value_type>
> *) {
return std::is_same<
A,
typename basic_data_object<String,Allocator>::template
array_container<typename A::value_type>
>::value;
}
template<typename A>
static constexpr bool test(...) {
return false;
}
static const bool value = test<T>(nullptr);
};
template <
typename String,
template<class> class Allocator,
typename T,
bool Select =
is_basic_data_object_array_container<T,String,Allocator>::value
>
struct get_data_object_value;
template <
typename String,
template<class> class Allocator,
typename T
>
struct get_data_object_value<
String,
Allocator,
T,
false
>
{
static void demo() {
std::cout << "Is NOT a basic_data_object array_container" << std::endl;
}
};
template <
typename String,
template<class> class Allocator,
typename T>
struct get_data_object_value<
String,
Allocator,
T,
true
>
{
static void demo() {
std::cout << "Is a basic_data_object array_container" << std::endl;
}
};
#include <list>
#include <memory>
using namespace std;
int main(int argc, char **argv)
{
get_data_object_value<string,allocator,std::vector<short>>::demo();
get_data_object_value<string,allocator,std::list<short>>::demo();
get_data_object_value<string,allocator,short>::demo();
return 0;
}
Built with gcc 4.8.2, clang 3.4. Output:
Is a basic_data_object array_container
Is NOT a basic_data_object array_container
Is NOT a basic_data_object array_container
VC++ 2013 will not compile this for lack of constexpr support. To accommodate that
compiler the following less natural implementation of the trait may be used:
template<typename T, typename String, template<class> class Allocator>
struct is_basic_data_object_array_container
{
template<typename A>
static
auto test(
std::is_same<
A,
typename basic_data_object<String, Allocator>::template
array_container<typename A::value_type>
> *
) ->
std::integral_constant<
bool,
std::is_same<
A,
typename basic_data_object<String, Allocator>::template
array_container<typename A::value_type>
>::value
>{}
template<typename A>
static std::false_type test(...);
using type = decltype(test<T>(nullptr));
static const bool value = type::value;
};

Edit: This answer only works because of a bug in GCC 4.8.1
Your code works as expected if you drop the keyword template in your specialization:
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
void foo() { std::cout << "general" << std::endl; }
};
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::array_container<T>>
// ^^^^^^ no template!
{
void foo() { std::cout << "special" << std::endl; }
};
Example tested with GCC 4.8.1:
int main() {
get_data_object_value<std::string,std::allocator,std::vector<int>> obj;
obj.foo(); // prints "special"
}

Related

How can I deduce the inner type of a nested std::vector at compile time?

The other day I asked a very similar question about nested vectors, but I've come across another problem that has me stumped. I need to get the innermost type of a nested vector at compile time so I can use it to pass as a template argument.
For example if I have this nested vector:
std::vector<std::vector<std::vector<int>>> v;
I need a way to extract int so I can call a function that takes a nested vector and works on the elements like this:
foo<int>(v);
Except the catch is this function should be able to work on nested vectors of any depth that hold any type. And when I call foo I want the inner type to be automatically deduced for me.
So maybe the call would look something like this:
foo<inner_type_t<v>>(v);
Where inner_type_t is some form of recursive template that resolves to int when given v.
I assume the solution will be similar to that of the other question but I haven't been able to work it out... I'm still a bit of a novice when it comes to recursive templates.
Edit:
Here is what I have so far...
template <typename T>
struct inner_type
{
using type = T;
};
template <typename T>
struct inner_type<std::vector<T>>
{
using type = inner_type<T>;
};
int main()
{
std::vector<std::vector<int>> v = {
{ 1, 2}, {3, 4}
};
std::cout << typeid(inner_type<decltype(v)>::type).name();
}
output:
struct inner_type<class std::vector<int,class std::allocator<int> > >
Wow I was really close haha, got it to work!
I just had to change the template specialization slightly to properly get the type recursively.
template <typename T>
struct inner_type
{
using type = T;
};
template <typename T>
struct inner_type<std::vector<T>>
{
// Had to change this line
using type = typename inner_type<T>::type;
};
int main()
{
std::vector<std::vector<int>> v = {
{ 1, 2}, {3, 4}
};
std::cout << typeid(inner_type<decltype(v)>::type).name();
}
Output:
int
#tjwrona1992's solution is ok, but doesn't allow for vectors with different allocators. Also, let's make this C++14-friendly with an _t version of the trait.
This should do the trick:
template <typename T> struct inner_type { using type = T; };
template<class T, class Alloc>
struct inner_type<std::vector<T, Alloc>> { using type = typename inner_type<T>::type; };
template<class T>
using inner_type_t = typename inner_type<T>::type;
Also, for the type name, you should using the type_name() function implemented here for C++14 or here for C++17.
See it working live...
A solution that follows the Bulletmagnet's suggestion to use value_type member type:
template<class T, typename = void>
struct inner_type {
using type = T;
};
template<class T>
struct inner_type<T, std::void_t<typename T::value_type>>
: inner_type<typename T::value_type> {};
template<class T>
using inner_type_t = typename inner_type<T>::type;
using VV = std::vector<std::vector<int>>;
static_assert(std::is_same_v<inner_type_t<VV>, int>);
A very good explanation of how std::void_t works, can be found in this question. It is used here to silently reject the specialization if typename T::value_type is ill-formed.
You can define the following primary class template, inner_type:
template<typename T>
struct inner_type {
using type = T;
};
which is used as the base case, i.e., for stopping the recursion – when the template argument doesn't match std::vector<T> (see below).
Then, the following convenience alias template just for writing C++14-like trailing _t instead of ::type:
template<typename T>
using inner_type_t = typename inner_type<T>::type;
Finally, the specialization for std::vector<T> – the recursive case:
template<typename T>
struct inner_type<std::vector<T>> {
using type = inner_type_t<T>;
};
This specialization is matched when passing an std::vector<T> as the template argument. Otherwise, the first one (see above) will be matched.
To check it. You can declare the following class template:
template<typename> struct type_shower;
Then:
auto main() -> int {
using type = inner_type_t<std::vector<std::vector<int>>>;
type_shower<type> _;
}
It should display an error saying that implicit instantiation of undefined template type_shower<int>. This implies that type is int.
With Boost.Mp11, this is a short one-liner (as always)
template <typename T>
using inner_value_t = mp_last<mp_iterate<T, mp_identity_t, mp_first>>;
Demo.
This is relying on the fact that you only care about vector, and the value type of vector<T, A> is just T. Which is actually true for all the sequence containers (this will work for list, deque, forward_list, etc, just fine. Although for vector<map<int, double>> it'll give you int).
If we had vector<vector<char>>, mp_iterate would first produce the sequence mp_list<vector<vector<char>>, vector<char>, char> (applying mp_first until we cannot anymore, and passing the result into mp_identity_t which is just a no-op). And then mp_last returns the last type in that list. Which is the type we want: char.
If you want to broaden to support arbitrary ranges, you can instead use std::ranges::range_value_t (C++20) instead of mp_first, which is the most generic solution.
Or, if you just care about the nested value_type alias:
template <typename T> using value_type_t = typename T::value_type;
template <typename T>
using inner_value_t = mp_last<mp_iterate<T, mp_identity_t, value_type_t>>;
Here's a more general solution. It works for list and forward_list too, not just vector.
#include <vector>
#include <list>
#include <forward_list>
#include <type_traits>
//using nested = std::vector<std::vector<std::vector<int>>>;
using nested = std::list<std::vector<std::forward_list<double>>>;
// primary template handles types that have no nested value_type member:
template< class, class = std::void_t<> >
struct has_vt : std::false_type { };
// specialization recognizes types that do have a nested value_type member:
template< class T >
struct has_vt<T, std::void_t<typename T::value_type>> : std::true_type { };
template <typename T, typename Enable = void> struct inner;
template <typename T> struct inner<T, typename std::enable_if<!has_vt<T>::value>::type> {
using vt = T;
};
template <typename T> struct inner<T, typename std::enable_if<has_vt<T>::value>::type> {
using vt = typename inner<typename T::value_type>::vt;
};
template<typename> struct type_shower;
int main() {
using deeep = inner<nested>::vt;
type_shower<deeep> _;
return 0;
}

Explicit match of template template arguments

Consider this function:
template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
std::cout << container.size() << std::endl;
}
Function foo() accepts a std::vector<T, Alloc>, but it also accepts std::map<Key, T, Compare, Allocator> in C++17 because Compare and Allocator have default values. Note std::map fails in C++14.
How can I make foo() only accept templates with exactly only 2 template parameters, so it fails with std::map in C++17?
How can I make foo only accept templates with exactly only 2 template parameters, so it fails with std::map in C++17?
If you want avoid that foo() accept a container accepting three or more template parameters (so fail with std::map) is relatively simple: you can follow the rtpax's suggestion or, given a custom type traits that say if a type is based on a two type accepting container
template <typename>
struct accept2 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
{ };
and a similar type traits for a three-accepting container
template <typename>
struct accept3 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
{ };
you can SFINAE enable foo() only if the deduced type accept two but doesn't accept three types
template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const & container)
{ }
But this solution (and also the rtpax one) has a problem: what about a container that receive before two template types and after one (or more) template not-type parameter with default values?
Or two template types and one (or more) template-template parameter with default values? Maybe with different signatures?
You can add specializations for accept3 to recognize the other cases but there are infinite combinations of type, non-type and template-template parameter, with default value, after the first two template type. So you have to write infinite specializations to intercept all cases.
This is a little unpractical.
And I suspect there isn't (in C++17) a practical solution.
Anyway, a full compiling example (avoiding three or more template types containers) follows
#include <map>
#include <vector>
#include <type_traits>
template <typename>
struct accept2 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
{ };
template <typename>
struct accept3 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
{ };
template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const &)
{ }
int main()
{
std::vector<int> v;
std::map<int,int> m;
foo(v); // compile
//foo(m); // compilation error
}
Create a template overload of the function that takes a container with three elements. When you try to use a container with two parameters it will work, when you try and use something with three parameters with the third having a default value (like std::map) it will fail (since either one could overload it).
#include <map>
#include <vector>
#include <iostream>
template<template<class, class, class> class C, class Key, class T, class Alloc>
void foo(C<Key, T, Alloc>& container) {
std::cout << "three arguments" << std::endl;
}
template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
std::cout << "two arguments" << std::endl;
}
int main() {
std::vector<int> v;
std::map<int,int> m;
foo(v);
foo(m);
}
note that this doesn't work if someone inputs something like
template <class A, class B, class C> bar{};
since that will only match the three parameter option, so it won't be ambiguous
Add one level of indirection that uses a variadic template argument instead of two fixed ones. Then you can use good old enable_if to disable it whenever the count is not two:
template<template<class...> class C, class T, class Alloc>
void foo_impl(C<T, Alloc>& container) {
std::cout << container.size() << std::endl;
}
template<template<class...> class C, class... Args>
std::enable_if_t<sizeof...(Args) == 2> foo(C<Args...>& container) {
foo_impl(container);
}
See here how it works.
Side note: Apparently, latest clang and latest msvc do not handle it correctly yet. clang (experimental concepts) does, however.

Deduce/erase type of template template argument

When using template template arguments how can I have the template type of the template template deduced or erased?
Consider the following SSCCE:
#include <cstdint>
#include <cstddef>
#include <iostream>
using namespace std;
template<int i>
struct Value { };
template<int i>
struct BadValue { };
template<typename... G>
struct Print;
template<template<int> class ValueType, int... Is>
struct Print< ValueType<Is>... > {
static void print() {
const int is[] = { Is... };
for (int i: is)
cout << i;
cout << endl;
}
};
using V1 = Value<1>;
using V2 = Value<2>;
using V3 = Value<3>;
using BV = BadValue<1>;
int main() {
Print<V2, V1, V2, V3>::print(); // <-- fine
Print<V2, V1, V2, BV>::print(); // <-- BV used by accident
}
Deducing the template<int> class ValueType argument of the Print class to a template class like the Value and BadValue classes enforces that all the template arguments in the parameter pack to the Print class are specializations of the same ValueType template class - this is intentional. That is, the second line in the main() function causes a compile-time error as the ValueType argument cannot be deduced to match both the Value and BadValue classes. If the user by accident tries to mix the templates when using the Print template a compile time error arises, which provides a bit of diagnostic.
The above implementation, however, still has the int type fixed for the inner template argument of the ValueType template template argument. How can I erase it and have it deduced as well?
Generally speaking, when deducing a template template argument, how can I access the inner template argument?
If I understand correctly, you want that Print<V2, V1, V2, VB>::print(); generate an error that is simpler to understand.
For this, the best I can imagine is to works with static_assert()s.
In this particular case -- Print is a struct with only a partial specialization implemented and no general version implemented -- a not really but simple solution is available: implement the general version to give a static_assert() error with a message of your choice.
By example
template <typename ... G>
struct Print
{
static_assert( sizeof...(G) == 0, "not same int container for Print<>");
static void print()
{ };
};
template <template<int> class ValueType, int ... Is>
struct Print< ValueType<Is>... >
{
static void print()
{
using unused = int const [];
(void)unused { (std::cout << Is, 0)... };
std::cout << std::endl;
}
};
Unfortunately this solution accept as valid Print<>; I don't know if is good for you.
Another (better, IMHO, but more elaborate) solution can be transform the Print partial specialization in a specialization that accept variadic int containers (variadic ValueTypes instead a fixed ValueType) and, in a static_assert(), check (with a custom type traits) that all containers are the same.
Bye example, with the following custom type traits
template <template <int> class ...>
struct sameCnts : public std::false_type
{ };
template <template <int> class C0>
struct sameCnts<C0> : public std::true_type
{ };
template <template <int> class C0, template <int> class ... Cs>
struct sameCnts<C0, C0, Cs...> : public sameCnts<C0, Cs...>
{ };
you can write the Print specialization as follows
template <template <int> class ... Cs, int ... Is>
struct Print< Cs<Is>... >
{
static_assert(sameCnts<Cs...>{}, "different containers in Print<>");
static void print()
{
using unused = int const [];
(void)unused { (std::cout << Is, 0)... };
std::cout << std::endl;
}
};
If you can use C++17, you can use folding and the type traits can be written
template <template <int> class, template <int> class>
struct sameCnt : public std::false_type
{ };
template <template <int> class C>
struct sameCnt<C, C> : public std::true_type
{ };
template <template <int> class C0, template <int> class ... Cs>
struct sameCnts
: public std::bool_constant<(sameCnt<C0, Cs>::value && ...)>
{ };
and (using folding also in print() method) Print as follows
template <template <int> class ... Cs, int ... Is>
struct Print< Cs<Is>... >
{
static_assert( sameCnts<Cs...>{}, "different containers in Print<>");
static void print()
{ (std::cout << ... << Is) << std::endl; }
};
-- EDIT --
The OP ask
But how can I have the Print class accept also, for example, types that are specialized for a double non-type value instead of the int non-type values?
Not sure to understand what do you want but (remembering that a double value can't be a template non-type parameter) I suppose you want a Print that accept types with non-types template parameter when the type of this non type template parameter isn't fixed as in your example (int).
For C++11 and C++14 I think that in necessary to explicit the type of the non type values.
I mean... If you write Print as follows
template <typename ...>
struct Print;
template <typename T, template <T> class ... Cs, T ... Is>
struct Print< T, Cs<Is>... >
{
static_assert(sameCnts<Cs...>{}, "different containers in Print<>");
// ...
};
you have to use it this way
Print<int, V2, V1, V2, V3>::print();
that is explicating int (or long, or whatever) as first template parameter. This because the int type can't be deduced.
Starting from C++17 you can use auto as type for non-type template parameter, so you can write Print as follows
template <typename ...>
struct Print;
template <template <auto> class ... Cs, auto ... Is>
struct Print< Cs<Is>... >
{
static_assert( sameCnts<Cs...>{}, "different containers in Print<>");
static void print()
{ (std::cout << ... << Is) << std::endl; }
};
and the is no need to explicit the type and you can write
Print<V2, V1, V2, V3>::print();
In this case, you have to use auto instead of int also in sameCnt and sameCnts.
If you work in C++17, you can declare non-type template parameter with auto, so simply declare Is as auto..., and use auto instead of int in the function definition as possible as you can.
Of course, since type of elements of Is may be different, it may be impossible to declare the array is. Instead, you can use std::tuple and print the tuple instead.
// print_tuple is used to print a tuple
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp)>::type
print_tuple(const std::tuple<Tp...>&)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp)>::type
print_tuple(const std::tuple<Tp...>& t)
{
std::cout << std::get<I>(t);
print_tuple<I + 1, Tp...>(t);
}
// ...
template<template<int> class ValueType, auto... Is>
// ^^^^
struct Print< ValueType<Is>... > {
static void print() {
print_tuple(std::make_tuple(Is...)); // make a tuple, and print it
}
};
LIVE EXAMPLE
The above pattern (making a tuple then dealing with the tuple) allows you to apply some complicated function to the parameter pack Is. However, if you only want to print the pack, you can alternatively use the C++17 feature fold expression instead, which is simpler.
template<template<int> class ValueType, auto... Is>
// ^^^^
struct Print< ValueType<Is>... > {
static void print() {
(std::cout << ... << Is); // fold expression, also C++17 feature
}
};
LIVE EXAMPLE

Class template specialization partial ordering and function synthesis

The rules for picking which class template specialization is preferred involve rewriting the specializations into function templates and determining which function template is more specialized via the ordering rules for function templates [temp.class.order]. Consider this example, then:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <class T, class U> struct A { };
template <class T> int foo(A<T, void_t<T>> ) { return 1; }
template <class T> int foo(A<T*, void> ) { return 2; }
int main() {
std::cout << foo(A<int*, void>{});
}
Both gcc and clang print 2 here. This makes sense with some previous examples - deducing against a non-deduced context (void against void_t<T>) is just ignored, so deducing <T, void_t<T>> against <X*, void> succeeds but deducing <T*, void> against <Y, void_t<Y>> fails in both arguments. Fine.
Now consider this generalization:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <int I> struct int_ { static constexpr int value = I; };
template <class T, class U> struct A : int_<0> { };
template <class T> struct A<T, void_t<T>> : int_<1> { };
template <class T> struct A<T*, void> : int_<2> { };
int main() {
std::cout << A<int*, void>::value << '\n';
}
Both clang and gcc report this specialization as ambiguous, between 1 and 2. But why? The synthesized function templates aren't ambiguous. What's the difference between these two cases?
Clang is being GCC-compatible (and compatible with existing code that depends on both of these behaviors).
Consider [temp.deduct.type]p1:
[...] an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
The crux of the issue is what "compatible" means here.
When partially ordering function templates, Clang merely deduces in both directions; if deduction succeeds in one direction but not the other, it assumes that means the result will be "compatible", and uses that as the ordering result.
When partially ordering class template partial specializations, however, Clang interprets "compatible" as meaning "the same". Therefore it only considers one partial specialization to be more specialized than another if substituting the deduced arguments from one of them into the other would reproduce the original partial specialization.
Changing either of these two to match the other breaks substantial amounts of real code. :(

Basic Typelist functionality

I'm having a bit of trouble getting my head around TypeLists or lists of templates. That is:
class nulltype{};
template <typename HEAD, typename TAIL>
struct tlist
{
typedef HEAD head;
typedef TAIL tail;
};
template <class TList>
class OutputClass
{
public:
void output(Type t)
{
std::cout << t << endl;
}
};
typedef tlist<double,tlist<float,NullType> > floatingPoints;
typedef tlist<std::string,tlist<char *,NullType> > text;
int main()
{
OutputClass<floatingPoints> floats = OutputClass<floatingPoints>();
floats.output(1.0f);
OutputClass<text> strings = OutputClass<text>();
floats.output("hello");
return 0;
}
Basically my goal is that I would like OutputClass.output to output the parameter passed to it, but only if that class instances typelist contains the type passed into the function. ie. referring to the above code: floats can only output float type and double type as defined by its typelist, "floatingPoints". Should a string or int get passed in, I would hope it would error.
I'm having one hell of a time trying to find any examples on how to do this, I've found the indexing examples and length examples a million times over, and they have helped me a lot, but i just can't seem to figure this last bit out. Any and all help will be appreciated.
We first need some helper templates. The first one checks if two types are the same:
template <typename T, typename U>
struct is_same
{
// Default case: T and U are not the same type
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
// Specialization: both template arguments are of the same type
static const bool value = true;
};
We can now use the boolean is_same<T, U>::value to determine if the types T and U are equivalent.
Using is_same we can now write a template which checks if a specific type occurs in a type list:
template <typename TList, typename T>
struct contains
{
static const bool value =
is_same<typename TList::head, T>::value // Base case
|| contains<typename TList::tail, T>::value; // Recursion
};
template <typename T>
struct contains<nulltype, T>
{
// Termination condition
static const bool value = false;
};
This is a recursive template, using a specialization on nulltype to terminate the recursion.
One final helper template we need is enable_if:
template <bool Cond, typename T=void>
struct enable_if
{
// Default case: Cond assumed to be false, no typedef
};
template <typename T>
struct enable_if<true, T>
{
// Specialization: Cond is true, so typedef
typedef T type;
};
enable_if<true, T>::type yields T, whereas enable_if<false, T>::type is undefined. We exploit the SFINAE rule the enable or disable a function depending on its template argument, like so:
template <typename TList>
class OutputClass
{
public:
// Only enable the function if T is in TList (by SFINAE)
template <typename T>
typename enable_if<contains<TList, T>::value>::type
output(T t)
{
std::cout << t << std::endl;
}
};
Quite a trip, but if you understand all of this you're well on your way to master template metaprogramming. For an in-depth discussion of template metaprogramming I recommend you pick up a copy of C++ Template Metaprogramming (ISBN-13: 9780321227256).