Consider the following code:
#include <iostream>
template <template<class...> class C>
struct foo {
foo() { std::cout << "base case\n";}
};
template <template<class> class C>
struct foo< C > {
foo() { std::cout << "single param case\n";}
};
template <template<class,class> class C>
struct foo< C > {
foo() { std::cout << "two param case\n";}
};
template <typename T> struct bar1 {};
template <typename T,typename U> struct bar2 {};
template <typename T,typename U,typename V> struct bar3 {};
template <typename...T> struct barN {};
int main() {
foo<bar1> f;
foo<bar2> g;
foo<bar3> h;
foo<barN> n;
}
Output is (gcc10.2#godbolt):
single param case
two param case
base case
base case
Suppose barX is given and that I have other templates with varying number of type parameters. Some variadic some not.
Is it possible to write a specialization that only matches the variadic template (barN in the above example)?
Very interesting question. Unfortunately the answer is No.
There is no general way to determine if a template had a template parameter pack or just a bunch of regular template parameters with or without defaults.
The reason is that non-variadic templates can bind to variadic template template parameters and the concrete types of a template can bind to a template parameter pack.
So effectively the information is not available via deduction/specialization. And in general this is good - without this feature variadic templates would lose much of their power.
But if we could limit the maximum length of template arguments we could write a trait with a bunch of template specializations. This works because of partial ordering (as you have shown in your question): godbolt
We can determine whether a class template that can be instantiated with 0 template arguments is genuinely variadic or (merely) has defaults for all its non-variadic template arguments, by counting the arguments to an 0-argument instantiation:
template<class> constexpr unsigned argc_v;
template<template<class...> class C, class... A> constexpr unsigned argc_v<C<A...>> = sizeof...(A);
template<template<class...> class, class = void> constexpr bool is_variadic_v = false;
template<template<class...> class C> constexpr bool is_variadic_v<C, std::void_t<C<>>> = argc_v<C<>> == 0;
Then we can use this to build a set of specializations that respectively accept only variadic, 1-argument (with possible default) and 2-argument (with possible default/s) class templates:
template<template<class...> class, class = std::true_type>
struct foo;
template<template<class...> class C>
struct foo<C, std::bool_constant<is_variadic_v<C>>> {
foo() { std::cout << "variable case\n"; }
};
template<template<class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void>> == 1>> {
foo() { std::cout << "single param case\n";}
};
template<template<class, class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void, void>> == 2>> {
foo() { std::cout << "two param case\n";}
};
I'm a bit disappointed that the latter argc_v tests are necessary (in C++20 mode); I think it's something to do with P0522 / CWG150.
Demo.
Related
I've been trying to answer the question in the title, but I'm stumped. Basically, trying to see if there's a built-in way to tell the 'source' of a template instantiation, at least for classes. Here is an example of what I'd like to do:
template<class T>
class A { };
auto a = A<int>();
template<class T>
auto someFunction(T item) {
if(/* if type of a is from the templated class A */) {
// yep A<int> is 'from' A.
}
}
Is this possible, in some way such as this? I could use some saved value or inheritance shenanigans to get something similar, but I'd rather not.
Maybe with a custom type traits.
Something as follows
template <typename>
struct is_A : public std::false_type
{ };
template <typename T>
struct is_A<A<T>> : public std::true_type
{ };
// ...
template <typename T>
auto someFunction(T item) {
if( is_A<T>::value ) {
// yep A<int> is 'from' A.
}
}
Or, maybe, you want intercept non only A<T> but, generically, all types that are a template type, maybe with an undefined number of template arguments?
In this case you can try with something as follows
template <typename>
struct is_template : public std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_template<C<Ts...>> : public std::true_type
{ };
Problem: this type traits intercepts all template types with types template arguments and only template arguments. But doesn't intercept, by example, std::integer_sequence<int, 0, 1, 2, 3, 4, 5> that receive a type and some non-type template parameter.
You can add other specializations for is_template, to intercept other cases, but you can't define a specialization that catch all template types (all combinations of template parameters).
Use a type trait
#include <type_traits>
#include <iostream>
template<class T>
class A { };
auto a = A<int>();
template <typename X>
struct is_from_A : std::false_type {};
template <typename T>
struct is_from_A<A<T>> : std::true_type {};
int main() {
std::cout << is_from_A<int>::value << "\n"; // 0
std::cout << is_from_A<A<int>>::value << "\n"; // 1
}
An other way, allowing derived class to match:
template <typename T>
std::true_type is_an_A_impl(A<T>*);
std::false_type is_an_A_impl(...);
template <typename T>
using is_a_A = decltype(is_an_A_impl(std::declval<T*>()));
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
I am trying to leave in struct A one function foo (prints 0) if its parameter have template method isA<void> and another (prints 1) if haven't. This code (reduced to minimal example below) compiles (tried with gcc 6.1.0 and clang-3.9.0 with explicit --std=c++14 option) and runs.
But it prints 1, though, I am sure, that it shall print 0. I wonder where am I wrong, but real question is: how to make this work correct?
Please only C++14 solutions.
#include <type_traits>
#include <iostream>
#include <utility>
using std::enable_if;
using std::declval;
using std::true_type;
using std::false_type;
using std::cout;
template<int M>
struct ObjectX
{
template<typename C>
bool isA() { return false; }
};
struct XX : ObjectX<23456> {
int af;
};
template <typename ObjType> using has_dep = decltype(declval<ObjType>().template isA<void>());
template <typename, typename = void>
struct has_isa : public false_type {};
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};
template<typename ObjType>
struct A
{
template<typename T = void>
typename enable_if<has_isa<ObjType>::value, T>::type
foo() {
cout << "called foo #0" << "\n";
}
template<typename T = void>
typename enable_if<!has_isa<ObjType>::value, T>::type
foo() {
cout << "called foo #1" << "\n";
}
};
int
main()
{
A<XX> axx;
// XX().template isA<void>(); -- to check, that we can call it and it exists
axx.foo();
return 0;
}
There are two problems in this program.
First, has_dep<XX> is bool. When we try has_dep<XX>, adding the default template arguments means this is really has_dep<XX, void>. But the specialization is has_dep<XX, bool> - which doesn't match what we're actually looking up. bool does not match void. That's why has_dep<XX> is false_type. The solution to this is std::void_t, and I'd suggest reading through that Q/A to get an idea for why it works. In your specialization, you need to use void_t<has_dep<ObjType>> instead.
Second, this is not right:
template<typename T = void>
typename enable_if<has_isa<ObjType>::value, T>::type
SFINAE only happens in the immediate context of substitution, and class template parameters are not in the immediate context of function template substitution. The right pattern here is:
template <typename T = ObjType> // default to class template parameter
enable_if_t<has_isa<T>> // use the function template parameter to SFINAE
foo() { ... }
Make those two fixes, and the program works as intended.
Your sfinae fails because has_isa chooses the wrong specialization.
The use of has_isa<T> must be either the default implementation or the specialized version.
As you defined, you have a default argument to void:
// default argument ---------v
template <typename, typename = void>
struct has_isa : public false_type {};
Then in the expression has_isa<T>, the second parameter must be void. It's roughly the same as writing has_isa<T, void>.
The problem is this:
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType>> : public true_type {};
// ^--- what's that type?
Even though template partial ordering would consider this "overload" more specialized, it won't be chosen. Look at the definition of has_dep:
struct XX {
template<typename C> bool isA() { return false; }
};
template <typename ObjType>
using has_dep = decltype(declval<ObjType>().template isA<void>());
Hey, that type has_dep<T> is the return type of t.isA<void>() which is bool!
So the specialized version look like this:
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType>> : public true_type {};
// ^--- really, this is bool in our case
So in order for this to work, you must call has_isa<T, bool>. As this is impractical, you should define your specialization as this:
template <typename ObjType>
struct has_isa<ObjType, void_t<has_dep<ObjType>>> : public true_type {};
Where void_t is defined as so:
template<typename...>
using void_t = void; // beware for msvc
As such, has_isa<T> will always consider the specialization, because we send void as the second template parameter, and now our specialization always result with void as second parameter.
Also, as stated by Barry, your function is not correctly formed as sfinae only appears in immediate context. You should write it like this:
template<typename T = ObjType>
typename enable_if<has_isa<T>::value, void>::type
foo() { // ^--- sfinae happens with T
cout << "called foo #0" << "\n";
}
If you don't wish to expose the template parameter, simply make the function private:
template<typename ObjType>
struct A {
public:
void foo() {
foo_impl();
}
private:
template<typename T = ObjType>
typename enable_if<has_isa<T>::value, void>::type
foo_impl() {
cout << "called foo #0" << "\n";
}
template<typename T = ObjType>
typename enable_if<!has_isa<T>::value, void>::type
foo_impl() {
cout << "called foo #1" << "\n";
}
};
Your problem is that you specialize the wrong class:
You should force has_dep to return void.
template <typename ObjType> using has_dep = decltype(static_cast<void>(declval<ObjType>().template isA<void>()));
So here
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};
// It is really <bjType, void> you specialize.
So, first consider the following where template parameters are known implicitly from the function arguments:
#include <iostream>
using namespace std;
class A {};
class B {};
template <class T1, class T2>
class C {
T1 a;
T2 b;
};
template <class T1>
class D {
T1 a;
};
template <template<class, class> class TC, class TA, class TB>
void foo(TC<TA, TB> c) {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD, class TA>
void foo(TD<TA> d){
std::cout << "T<T>" << std::endl;
};
int main() {
C<A,B> c;
D<A> d;
foo(c);
foo(d);
}
And output is as you'd expect:
T<T,T>
T<T>
However, what if I don't have an instance of class C and D so I need to explicitly call the correct overload? How would this be done? i.e., have a main() that consists of:
int main() {
foo<C<A,B> >();
foo<D<A> >();
}
I've experimented with a few overloads of foo() as shown below:
template <template<class, class> class TC>
void foo() {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD>
void foo(){
std::cout << "T<T>" << std::endl;
};
template <template<class, class> class TC, class TA, class TB>
void foo() {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD, class TA>
void foo(){
std::cout << "T<T>" << std::endl;
};
However, this (and all permutations I've been able to think of) simply results in a series of errors along the lines of the (abbreviated) output shown below
prog.cpp: In function 'int main()':
prog.cpp:44:18: error: no matching function for call to 'foo()'
foo<C<A,B> >();
^
prog.cpp:44:18: note: candidates are:
prog.cpp:19:6: note: template<template<class, class> class TC> void foo()
void foo() {
^
prog.cpp:19:6: note: template argument deduction/substitution failed:
prog.cpp:24:6: note: template<template<class> class TD> void foo()
void foo(){
^
prog.cpp:24:6: note: template argument deduction/substitution failed:
Is what I'm looking to do even allowable? If so, where am I messing up?
---- EDIT ----
So as apple apple pointed out if my main() is as follows:
int main() {
foo<C, A, B>();
foo<D, A>();
}
I get the output as expected.
However, my real-world case winds up being more complex. I'll expand a bit here. The legacy code has (hundreds) of typedefs defined in headers elsewhere along the lines of:
typedef C<A, B> type_117;
typedef D<A> type_252;
The class I'm working on is templated and is instantiated with one of those typedefs as the templating argument. So something along the lines of:
template <class Type>
class Test
{
public:
Test();
SomeClass mSC;
}
Test::Test()
: mSC(foo<Type>())
{
};
where Test was instantiated as
Test<type_117> aTest;
So I've been trying to figure out how to write foo() for this context. At the point I call foo() within my Test's initializer am I able to "decompose" it to produce the <C,A,B> form? Or have I hit a roadblock and need to rework some of the existing framework?
template<class T>struct tag_t{constexpr tag_t(){}};
template<class T>constexpr tag_t<T> tag{};
these are type tags. They can be passed to functions without an instance of the type.
Template functions will deduce on them.
template <template<class, class> class TC, class TA, class TB>
void foo(tag_t<TC<TA, TB>>) {
std::cout << "T<T,T>" << std::endl;
};
template <template<class> class TD, class TA>
void foo(tag_t<TD<TA>>){
std::cout << "T<T>" << std::endl;
};
at call site do foo(tag<type_117>) and bob, as they say, is your uncle.
In C++98 (ick):
template<class T>struct tag_t{};
foo(tag_t<type_117>());
You may use partial specialization (and variadic template):
template <class Type>
class Test;
template <template <typename ...> class C, typename ... Ts>
class Test<C<Ts...>>
{
public:
Test() : mSC(foo<C, Ts...>()) {}
SomeClass mSC;
};
Take in count that partial specialization is forbidden for functions; so is difficult to do what do you exactly asked.
The suggestion from apple apple (chenge the calling as foo<C, A, B>() is a good one but, if you want to maintain the original call (foo<C<A, B>>()) you can use the fact that the partial specialization is allowed for structs/classes and create a partial specialization for a functor; something like
template <typename>
struct bar;
template <template<typename, typename> class Tc, typename Ta, typename Tb>
struct bar<Tc<Ta,Tb>>
{
void operator() ()
{ std::cout << "bar<Tc<Ta, Tb>>()" << std::endl; }
};
template <template<typename> class Tc, typename Ta>
struct bar<Tc<Ta>>
{
void operator() ()
{ std::cout << "bar<Tc<Ta>>()" << std::endl; }
};
The problem (?) is that, calling it, you can't call as bar<C<A,B>>() od bar<D<A>>() but you have to add a couple of parentheses:
bar<C<A,B>>()();
bar<D<A>>()();
or
bar<C<A,B>>{}();
bar<D<A>>{}();
I suppose that the functor solution can solve also the problem of the Edit part of your question.
If the added couple of parentheses is a problem, you can (as suggested by Jarod42 (thanks!)) wrap the call in a template function, as follows
template <typename T>
void bar ()
{ bar<T>{}(); }
So you can call the bar<C<A, B>>() function and manage the call in the specialized bar<C<A, B>> struct.
Observe also the solution from Jarod42: depending on your requirements, you could develop only a version of the partial specialization of bar.
-- EDIT --
The OP ask
I'm not that familiar with partial specialization; could you expand a bit on how what I was trying was?
Specialization (partial and full) is a big, big topic.
Just some example, to give an idea.
Given a template class/struct
template <typename X, typename Y>
struct foo
{ };
you can partial specialize it as follows (by example)
template <typename X>
struct foo<X, X>
{ };
when the specialization maintain a template variable, or you can full specialize as follow (by example)
template <>
struct foo<int, long>
{ };
where all template argument are fixed.
Well: with function you can full specialize but not partial specialize.
So you can write a template function
template <typename X, template Y>
void foo ()
{ }
and full specialize it
template <>
void foo<int, long> ()
{ }
but you can't partial specialize it; so you can't write (is an error)
template <typename X>
void foo<X, X> ()
{ }
Possibly easy to solve, but its hard to find a solution to this:
Is it possible to (partially) specialize for a whole set of types?
In the example "Foo" should be partially specialized for (T,int) and (T,double) with only one template definition.
What I can do is define a specialisation for (T,int). See below. But, it should be for (T,int) and (T,double) with only one function definition (no code doubling).
template <typename T,typename T2>
struct Foo
{
static inline void apply(T a, T2 b)
{
cout << "we are in the generic template definition" << endl;
}
};
// partial (T,*)
template <typename T>
struct Foo<T, int > // here something needed like T2=(int, double)
{
static inline void apply(T a, T2 b)
{
cout << "we are in the partial specialisation for (T,int)" << endl;
}
};
Any ideas how to partially specialize this for (T,int) and (T,double) with one template definition?
If I understood your question correctly, then you can write a base class template and derive from it, as illustrated below:
template <typename T, typename U>
struct Foo_Base
{
static inline void apply(T a)
{
cout << "we are in the partial specialisation Foo_Base(T)" << endl;
}
};
template <typename T>
struct Foo<T, int> : Foo_Base<T, int> {};
template <typename T>
struct Foo<T, double> : Foo_Base<T, double> {};
Although its not one template definition (as you asked for), but you can avoid the code duplication.
Demo : http://www.ideone.com/s4anA
I believe you could do this using Boost's enable_if to enable the partial specialisation for just the types you want. Section 3.1 shows how, and gives this example:
template <class T, class Enable = void>
class A { ... };
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };