Violet Giraffe
Violet Giraffe

Reputation: 33607

Template instantiation parse error

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

Answers (1)

Barry
Barry

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:

  1. You cannot 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.
  2. 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

Related Questions