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
.