Ошибка разбора экземпляра шаблона - PullRequest
0 голосов
/ 14 мая 2018

Извините за смутное название, я недостаточно хорошо понимаю проблему, чтобы даже правильно ее описать. Я просто не понимаю, почему последняя строка моего 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 голосов
/ 14 мая 2018

Этот код основан на убеждении, что в стандарте указано dynamic_cast как-то так:

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

Но хотя четыре приведения C ++ могут выглядеть как вызовы шаблонов функций, онина самом деле часть основного языка.Это ключевые слова, они не ограничены областью, и хотя они принимают одну вещь, которая выглядит как параметр типа шаблона, на самом деле это не так.

Так что &std::dynamic_cast<B, A> не имеет никакого смысла.std::dynamic_cast вообще нет, dynamic_cast принимает только один аргумент типа, и вы не можете иметь "частичное" динамическое приведение - выражение dynamic_cast<T>(v), вы не можете просто иметь dynamic_cast<T> само по себе.

Предполагая, что std::dynamic_cast является просто опечаткой для std::dynamic_pointer_cast, с вашим кодом есть еще две проблемы:

  1. Вы не можете dynamic_cast и A* до B*.Вы можете upcast неполиморфные объекты / указатели, но вы не можете downcast их.Так что это неправильно.
  2. Если вы сделаете A полиморфным, проблема станет более понятной, если вы немного уменьшите его и сделаете базовый вариант ObjFirst неполным вместо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;
    }
    

Это не скомпилируется с ошибкой:

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 теперь является частью системы типов.

...