Reputation: 139
I have a class parameterized by a template template class with a static member function:
template <template <typename> class F>
struct A {
static int foo();
};
This class has no default definition for foo
, and must be specialized for different types.
I also have another class parameterized by a template template class with a nested template class:
template <template <typename> class F>
struct B {
template <typename T>
struct C {};
};
I want C
to specialize A
for any template template class F
that specializes A
already:
template <template <typename> class F>
struct A<B<F>::template C> {
static int foo();
};
template <template <typename> class F>
int A<B<F>::template C>::foo() {
return A<F>::foo() / 2;
}
So, if I have a class that specializes A
:
template <typename T>
struct E {};
template <>
int A<E>::foo() {
return 42;
}
I would expect to be able to use the specialization like this (and return 21):
int bar() {
return A<B<E>::template C>::foo();
}
However, this fails to link - it cannot find the reference to A<B<E>::C>::foo()
.
(Note that all of this is in a single file - there is nothing weird happening with headers here)
It appears that the compiler is trying to use the primary template for A
rather than the specialization, which means that foo
is undefined. Why does it not use the specialization in this instance?
template <template <typename> class F>
struct A {
static int foo();
};
template <template <typename> class F>
struct B {
template <typename T>
struct C {};
};
template <template <typename> class F>
struct A<B<F>::template C> {
static int foo();
};
template <template <typename> class F>
int A<B<F>::template C>::foo() {
return A<F>::foo() / 2;
}
template <typename T>
struct E {};
template <>
int A<E>::foo() {
return 42;
}
int bar() {
// Link fails - error: undefined reference to 'A<B<E>::C>::foo()'
return A<B<E>::template C>::foo();
}
Upvotes: 4
Views: 404
Reputation: 275220
template<class T>
struct A {};
template<class T>
struct B {
using type=T;
};
template<class T>
struct A<typename B<T>::type> {};
this is basically the same but with 1 fewer template layer.
This doesn't work either.
The problem is that B<T>::type
or B<T>::template Z
or whatever is, in the general case, an arbitrary compile-time function.
And in order to pattern match against it, we need to invert this arbitrary compile time function.
The standard says "compilers don't have to do that", which is one of the few sane things you can do here. It definitely says this for types; for templates, well, the standard's wording for template template parameters is often missing details, so I wouldn't be surprised if the wording was missing. But if it doesn't, it would be a bug in the standard.
In order to go from
template<class T>
struct A<typename B<T>::type> {};
to see if A<foo>
matches it, it would have to test all types T
to see which of them have a B<T>::type
that equals foo
.
This may not be what you intend to ask, but that is what you are asking for.
The same is true of your template example.
template <template <typename> class F>
struct A<B<F>::template C> {
static int foo();
};
you are asking for the compiler to check every type F
such that if you pass it to the arbitrary template B<>
then evaluated ::C
in it, does the template match what you are passing A
.
First fun case:
template<class X>
struct C0 {};
template <template <typename> class F>
struct B {
template <typename T>
using C=C0<X>:
};
now, what is F
in A<C0>
? Every single F
qualifies.
template<class X>
struct C0 {};
template <template <typename> class F, class=void>
struct B {
template <typename T>
using C=C0<X>:
};
template<class X>
struct C1 {};
template <template <typename> class F, class=void>
struct B<
F,
std::enable_if_t<
proves_collatz_conjecture( F<int>::value )
>
> {
template <typename T>
using C=C1<T>;
};
now to pattern mach A<C0>
the compiler must produce the F
such that F<int>::value
is a compile-time type that when passed to proves_collatz_conjecture
returns true
at compile time.
That would be useful.
template specialization is pattern matching. In C++ you cannot pattern match against dependent types (and presumably templates) as neither types nor templates have identity beyond their value.
You cannot inspect the scope that a variable, type or template is defined in. So you cannot pattern match that either.
If you want do do what you want, the template C
has to itself have a property you can inspect and test against.
Upvotes: 5