Специализация шаблона функции-члена после ошибки создания экземпляра и порядок функций-членов

Следующий бит кода не скомпилируется в gcc 4.5.3

struct Frobnigator
{
    template<typename T>
    void foo();

    template<typename T>
    void bar(); 
};

template<typename T>
void Frobnigator::bar()
{
}

template<typename T>
void Frobnigator::foo()
{
    bar<T>();
}

template<>      // error
void Frobnigator::foo<bool>()
{
    bar<bool>();
}

template<>
void Frobnigator::bar<bool>()
{
}

int main()
{
}

Сообщение об ошибке: specialization of ‘void Frobnigator::bar() [with T = bool]’ after instantiation. Я, наконец, решил эту проблему, указав специализацию Frobnigator::bar<bool>() перед Frobnigator::foo<bool>(). Очевидно, порядок, в котором эти методы кажутся важными.

Почему тогда следующая следующая версия вышеприведенного кода, в которой после универсальной версии появляется спецификация bar, действительная?

struct Frobnigator
{
    template<typename T>
    void foo();
};

template<typename T>
void Frobnigator::bar()
{
}

template<>
void Frobnigator::bar<bool>()
{
}

int main()
{
}

Ответ 1

Ваш первый код не соответствует стандарту.

n3376 14.7.3/6

Если шаблон, шаблон-член или член шаблона класса явно специализирован, то специализация должна быть объявлена ​​до первого использования этой специализации, которая приведет к неявному экземпляру проходить, в каждой единицы перевода, в которой такое использование происходит; диагностика не требуется.

В вашем случае - неявное создание функции bar с типом bool требуется по его использованию в foo<bool>, перед явным объявлением специализации.

Ответ 2

Ясно, что порядок, в котором эти методы появляются, имеет значение.

Действительно, как это обычно бывает в С++, вы не можете использовать что-то до его объявления, и это относится к явным специализациям шаблонов, а также к большинству других вещей.

Использование bar<bool> (путем вызова его из foo<bool>) без предыдущего объявления явной специализации приводит к тому, что специализация будет создана из общего шаблона, если она еще не была. Для предотвращения этого вам потребуется хотя бы объявление явной специализации.

Почему это так, учитывая, что специализация bar появляется после общей версии в следующей облегченной версии вышеуказанного кода

Второй пример отличается тем, что он не создает экземпляр foo<bool>. Проблема заключается не в том, что специализация объявляется после шаблона generic (который должен иметь место), но он объявлен после того, как эта специализация уже была создана.