2015-12-14 2 views
2

я не могу показаться, чтобы обернуть мою голову вокруг того, что происходит неправильно в следующем примереШаблон провал специализации для шаблонного структуры внутри шаблонного структуры

$ cat repro.cpp 
#include <iostream> 
using namespace std; 

template<class T> 
struct S1_t 
{ 
    template<class U> 
    struct inner_t_x {}; 

    using inner_t = inner_t_x<T>; 
}; 
struct Bogus{}; 

template<class T> 
struct Functor1 
{ 
    void operator()() 
    { 
    cout << "Functor1 FAIL: " << __PRETTY_FUNCTION__ << endl; 
    } 
}; 
template<> 
struct Functor1<typename S1_t<Bogus>::template inner_t_x<Bogus>> 
{ 
    void operator()() 
    { 
    cout << "Functor1 PASS: " << __PRETTY_FUNCTION__ << endl; 
    } 
}; 

template<class T> 
struct Functor2 
{ 
    void operator()() 
    { 
    cout << "Functor2 FAIL: " << __PRETTY_FUNCTION__ << endl; 
    } 
}; 
template<class T> 
struct Functor2<typename S1_t<T>::template inner_t_x<T>> 
{ 
    void operator()() 
    { 
    cout << "Functor2 PASS: " << __PRETTY_FUNCTION__ << endl; 
    } 
}; 

template<class T> 
void eval() 
{ 
    Functor1<T>{}(); 
    Functor2<T>{}(); 
} 

int main() 
{ 
    eval<S1_t<Bogus>::inner_t>(); 
    return 0; 
} 

$ clang++ repro.cpp -std=c++11 -Wall && ./a.out 
Functor1 PASS: void Functor1<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() 
Functor2 FAIL: void Functor2<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() [T = S1_t<Bogus>::inner_t_x<Bogus>] 

В Functor1, я явно специализируются на типе Bogus , и действительно, эта специализация используется. Однако в Functor2 тип разрешен для вывода, но частичная специализация терпит неудачу, и вместо этого генерируется общий шаблон. Тем не менее, __PRETTY_PRINT__ показать эту же подпись для Functor1 & Functort2 при создании экземпляра.

Может ли кто-нибудь объяснить это поведение, и есть ли способ исправить Functor2 частичную специализацию в соответствии с этим сценарием? Благодаря!

FWIW: Изменение Functor2 частичной специализации для

template<class T> 
struct Functor2<typename S1_t<Bogus>::template inner_t_x<T>> 
{ 
    void operator()() 
    { 
    cout << "Functor2 PASS: " << __PRETTY_FUNCTION__ << endl; 
    } 
}; 

дает правильный вывод

Functor1 PASS: void Functor1<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() 
Functor2 PASS: void Functor2<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() [T = S1_t<Bogus>::inner_t_x<Bogus>] 

, но это не вариант в моем случае использования.

+0

Почему нет шаблона 'template <> struct Functor1'? Может быть, это просто какой-то специальный синтаксис, который я не знаю, но выглядит странно. – maddin45

ответ

2

никогда не будет использоваться специализация Functor2. Компилятор может предупредить об этом, но это не так. Причина этого быть не выводима проста: Представьте, что вы позже добавить что-то вроде

struct Hogus; 

template<> 
struct S1_t<Hogus> 
{ 
    template <typename U> 
    using inner_t_x = S1_t<Bogus>::inner_t_x<Bogus>; 
}; 

затем S1_t<Bogus>::inner_t_x<Bogus> и S1_t<Hogus>::inner_t_x<Hogus> будет иметь тот же тип. Поэтому шаблонный вычет в частичной специализации Functor2 может давать как T=Bogus, так и T=Hogus. Следовательно, он не может быть выведен однозначно ни при каких обстоятельствах.

+0

Спасибо за яркий пример, внезапно это становится очень ясным. Есть ли какой-либо способ затем вывести тип T однозначно? Я чувствую, что нет, но я не уверен на 100%. – eg0x20

+0

Я не думаю, что есть способ вывести внешнюю и внутреннюю часть вложенного типа. Сожалею. –

4

typename S1_t<T>::.. не выводится при частичной специализации.

Вы получили четкое сообщение об ошибке при использовании inner_t вместо inner_t_x<T>:

main.cpp:41:8: error: template parameters not deducible in partial specialization: 

struct Functor2<typename S1_t<T>::inner_t> 

     ^

main.cpp:41:8: note:   'T' 

main.cpp:41:8: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used 
+0

Ссылки (относительно n4567): [temp.deduct.type] p5 и p6 * "Если имя типа указано таким образом, который включает в себя невыводимый контекст, все типы, которые содержат , это имя типа также не является -ducuc. "* – dyp

+0

Другими словами, нет абсолютно никакого способа вывести Т. Это правильно? Если это так, я был введен в заблуждение из-за отсутствия предупреждения о компиляторе, думая, что вывод типа был сделан на :: inner_t_x. – eg0x20

Смежные вопросы