Reputation: 33607
Sorry for the vague title, I don't understand the problem well enough to even describe it properly. I just don't understand why the last line of my main()
fails. A moderate amount of nested template calls follows:
#include <iostream>
template <typename T>
struct ObjFirst {};
template <typename T>
struct ObjFirst<void (*)(T)> {
template <void (*fp)(T)>
static void f(T arg){fp(arg);}
};
template <typename T, typename R>
struct ObjFirst<R (*)(T)> {
template <R (*fp)(T)>
static R f(T arg) {return fp(arg);}
};
template <typename T>
struct Id {
template <T fn_ptr>
static auto funcPtr(void) { return &ObjFirst<T>::template f<fn_ptr>; }
};
template <auto fn>
auto id() { return Id<decltype(fn)>::template funcPtr<fn>(); }
int sampleFunc(int) {return 0;}
void sampleFuncV(int) {}
template <typename R, typename T>
R sampleFuncT(T) {return R{};}
template <>
int sampleFuncT<int, int>(int i) {return 7+i;}
struct A {};
struct B : A {};
int main()
{
id<&sampleFunc>(); // OK
id<&sampleFuncV>(); // OK
auto f = id<&sampleFuncT<int, int>>(); // OK
std::cout << f(3); // Double-checking the result: OK ('10')
id<&std::dynamic_cast<B, A>>();
return 0;
}
You can see that a couple various calls of this template mechanism work, but the last one doesn't . For a moment I thought it's because the wrong ObjFirst
specialization is selected for a templated argument, but nope.
Any ideas appreciated. The failure can be observed with GCC 7, GCC 8 and clang 6: https://godbolt.org/g/5spCJy
Upvotes: 0
Views: 74
Reputation: 303870
This code is based on the belief that dynamic_cast
is specified in the standard as something like this:
namespace std {
template <typename Dst, typename Src>
Dst* dynamic_cast(Src* );
}
But while the four C++ casts may look like invocations of function templates, they're actually part of the core language. These are keywords, they're unscoped, and while they accept one thing that looks like a template type parameter, it's actually not.
So &std::dynamic_cast<B, A>
doesn't make any sense. There's no std::dynamic_cast
at all, dynamic_cast
only takes one type argument, and you can't have a "partial" dynamic cast - the expression is dynamic_cast<T>(v)
, you cannot just have dynamic_cast<T>
by itself.
Assuming std::dynamic_cast
is just a typo for std::dynamic_pointer_cast
, there are two other problems with your code:
dynamic_cast
an A*
to a B*
. You can upcast non-polymorphic objects/pointers, but you can't downcast them. So this is ill-formed.If you make A
polymorphic, the problem is would become more clear if you reduce it a little bit and make the base case of ObjFirst
incomplete instead of empty:
template <typename T>
struct ObjFirst;
template <typename T>
struct ObjFirst<void (*)(T)> {};
template <typename T, typename R>
struct ObjFirst<R (*)(T)> {} ;
struct B { };
struct D : B { };
int main()
{
ObjFirst<decltype(&std::dynamic_pointer_cast<B,D>)> n;
}
This fails to compile with the error:
foo.cxx: In function ‘int main()’:
foo.cxx:18:57: error: aggregate ‘ObjFirst<std::shared_ptr<B> (*)(const std::shared_ptr<D>&) noexcept> n’ has incomplete type and cannot be defined
ObjFirst<decltype(&std::dynamic_pointer_cast<B,D>)> n;
^
Which should make it clear what the problem is. You have specializations for void(*)(T)
and R(*)(T)
, but std::dynamic_pointer_cast
doesn't match either because noexcept
is now part of the type system.
Upvotes: 3