2017-02-22 22 views
4

У меня есть следующий код:Неоднозначность частичная специализация в зависимости от станд :: enable_if

#include <iostream> 

template <class T, typename U = void> class A; 

template <class T> 
class C 
{ 
public: 
    typedef T Var_t; 
}; 

template <class T> 
class B : public C<T> 
{ 
}; 

template <class T> 
class A<B<T>> 
{ 
public: 
    A() { std::cout << "Here." << std::endl; } 
}; 

template <class T> 
class A<T, typename std::enable_if< 
          std::is_base_of<C<typename T::Var_t>, T>::value> 
          ::type> 
{ 
public: 
    A() { std::cout << "There." << std::endl;} 
}; 

int main() 
{ 
    A<B<int>> a; 
    return 0; 
} 

Когда компилятор пытается создать экземпляр второй частичной специализации с параметром B<int>, std::is_base_of<C<int>, B<int>>::value является true, и поэтому std::enable_if<...>::type возвращается void (тип по умолчанию, если он не указан). Это вызывает ошибку «неоднозначной частичной специализации», поскольку компилятор не может решить между первой и второй частичными специализациями. Все идет нормально. Однако, когда я заменяю код в std::enable_if, просто должен быть true (то есть вторая частичная специализация равна template <class T> class A<T, typename std::enable_if<true>::type>), код компилируется и запускается. Он выводит "Here", указав первую специализацию.

Мой вопрос: если вы оба оцените до void, в конце концов, почему поведение std::enable_if<true>::type отличается от std::enable_if<std::is_base_of<...>::value>::type?

Это поведение было протестировано и проверено на Ideone here.

ответ

1

В std::enable_if<true>::type случае ваш код определяет два специализации класса А именно:

  1. A<B<T>, void>
  2. A<T, std::enable_if<true>::type>.

Эти две специализации весьма отличаются друг от друга. Первая специализация узко ориентирована на тип B<T>, тогда как вторая специализация является более общей для любого типа. Кроме того, во второй специализации выражение std::enable_if не зависит от T.

Для любого заявления A<X> a; тип X будет либо соответствовать B<something>, либо нет. Если он соответствует B<something>, тогда первая специализация будет использоваться, потому что она «более специализирована». Если X не соответствует B<something>, тогда будет использоваться вторая, более общая специализация. В любом случае вы не получите двусмысленную ошибку.

Для получения более подробной информации смотрите обсуждение частичной упорядоченности в partial template specialization

Теперь давайте рассмотрим std::enable_if<std::is_base_of<...>::value>::type случай.

У вас еще есть две специализации, но вторая специализация теперь обусловлена ​​enable_if, который, в свою очередь, зависит от параметра T.

  1. A<B<T>, void>
  2. A<T, std::enable_if<...>>.

Тип B<int> теперь соответствует обеим специализациям (в некоторой степени). Очевидно, что он соответствует специализации A<B<T>>, void>, но он также соответствует специализации A<T, std::enable_if...>>, потому что B<int> - это тип, который удовлетворяет условиям, налагаемым выражением std::enable_if.

Это дает вам две одинаково важные специализации, которые являются кандидатами на объявление переменной a, и поэтому вы получаете ошибку «неоднозначной частичной специализации».

Это может помочь сделать все это немного более конкретным, если вы добавили еще два заявления в main

A<C<int>> x; 
A<int> y; 

В std::enable_if<true> случае это будет компилировать и оба заявления будут называть «там» конструктор.

В более сложном случае объявление x будет компилировать и вызывать конструктор «там», но объявление y получит ошибку компилятора.

Нет int::Var_t, поэтому выражение std::enable_if получит отказ замены, и SFINAE скроет эту специализацию. Это означает, что не будет никакой специализации, которая бы соответствовала int, и вы получите сообщение об ошибке aggregate ‘A<int> y’ has incomplete type and cannot be defined

+0

Итак, вы говорите, что тот факт, что у меня есть специализация на 'T', делает компилятор« A > »и 'A >' быть одинаково специализированным? Это немного противоречит интуиции (я все еще ожидал бы, что 'A >' будет более специализированным, каким-то образом), но это имеет смысл. –

+0

Я согласен, что на первый взгляд кажется немного контр-интуитивным, но я думаю, что, возможно, это мы читаем больше в коде, чем должны? Мы видим 'B ' и думаем «весь класс, построенный вокруг' T', который должен быть более важным, чем некоторый тип, возвращаемый 'std :: enable_if'. Но в тесте enable также используется' B '. считается менее значительным, чем другое использование? –

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