Специализация шаблонов с преобразованием типов

Я нашел этот кусок фиктивного кода (надуманный пример ниже):

template <int I, typename T>
struct foo
{
    static int bar()
    {
        return 1;
    }
};

template <std::size_t Index, typename T>
struct foo<Index, T*>
{
    static int bar()
    {
        return 2;
    }
};

Обратите внимание, что специализация использует другой тип (по ошибке). Удивительно, что он компилируется без каких-либо ошибок (или предупреждений) как с GCC 4.8.1, так и с Clang 3.4. Но что еще более странно для линии GCC foo<0, int*>::bar() приводит к 1, но Clang дает 2. Что происходит? Он по-прежнему считается специализацией по стандарту?

Ответы

Ответ 1

Gcc ошибочен, потому что вы просто не можете назвать эту специализацию. Просто удалите определение основного шаблона:

template <int I, typename T>
struct foo;

template <std::size_t Index>
struct foo<Index, int*> {
  static int bar() {
    return 2;
  }
};

int main() {
  std::cout << foo<std::size_t(0), int*>::bar() << std::endl; // nope, not work
  std::cout << foo<0, int*>::bar() << std::endl; // nope, not work
}

Смотрите live пример. И этот код должен сообщать о двусмысленной частичной специализации, но это не (для gcc). Clang отчет "неоднозначный" .

PS Я утверждаю, что эта часть не достаточно покрыта стандартом.

Обновление

clang в этой ситуации не будет работать с перечислениями, .

Ответ 2

Ограничения на частичную специализацию шаблона класса в аргументе шаблона несимметричного шаблона 14.5.5 [temp.class.spec] в пункте 8 перечисляют следующее ограничение:

Частично специализированное несимвольное выражение аргумента не должно включать параметр шаблона частичной специализации, за исключением случаев, когда выражение аргумента является простым идентификатором.

Выражение аргумента с использованием size_t включает преобразование в int (size_t не указано) и, следовательно, не является простым идентификатором.