模板实例化解析错误

时间:2018-05-14 14:43:43

标签: c++ templates gcc clang c++17

对于模糊的标题感到抱歉,我不能很好地理解这个问题,甚至不能正确描述它。我只是不明白为什么main()的最后一行失败了。下面是适量的嵌套模板调用:

#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;
}

你可以看到这个模板机制的几个不同的调用工作,但最后一个没有。有一刻我认为这是因为为模板化参数选择了错误的ObjFirst专门化,但是没有。

任何想法都赞赏。使用GCC 7,GCC 8和clang 6可以观察到失败:https://godbolt.org/g/5spCJy

1 个答案:

答案 0 :(得分:3)

此代码基于以下信念:dynamic_cast在标准中指定为:

namespace std {
    template <typename Dst, typename Src>
    Dst* dynamic_cast(Src* );
}

虽然四个C ++演员可能看起来像函数模板的调用,但它们实际上是核心语言的一部分。这些是关键字,它们是无范围的,虽然它们接受一个看起来像模板类型参数的东西,但它实际上不是。

所以&std::dynamic_cast<B, A>没有任何意义。根本没有std::dynamic_castdynamic_cast只接受一个类型参数,并且您不能进行“部分”动态转换 - 表达式为dynamic_cast<T>(v),您不能只有dynamic_cast<T> }} 通过它自己。

假设std::dynamic_cast只是std::dynamic_pointer_cast的拼写错误,您的代码还有两个问题:

  1. 您不能dynamic_cast A*B*。你可以 upcast 非多态对象/指针,但你不能向下转换它们。所以这是不正确的。
  2. 如果你使A多态,那么如果你稍微减少它并使ObjFirst的基本情况不完整而不是空,问题会变得更清楚:

    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;
    }
    
  3. 无法使用错误进行编译:

    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;
                                                             ^
    

    哪个应该清楚问题是什么。您有void(*)(T)R(*)(T)的专精,但std::dynamic_pointer_cast不匹配,因为noexcept现在是类型系统的一部分。