Специализация класса шаблона без объявления в заголовке

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

foo.h:

template<typename T>
class Foo {
public:
  static int bar();
};

foo.cc:

#include "foo.h"

template<>
int Foo<int>::bar() {
  return 1;
}

template<>
int Foo<double>::bar() {
  return 2;
}

main.cc:

#include <iostream>
#include "foo.h"

int main(int argc, char **argv) {
  std::cout << Foo<int>::bar() << std::endl;
  std::cout << Foo<double>::bar() << std::endl;
  return 0;
}

Эта программа успешно компилирует и связывает gcc 4.7.2 для всех стандартов С++ (С++ 98, gnu ++ 98, С++ 11 и gnu ++ 11). Выход:

1
2

Это имеет смысл для меня. Поскольку блок трансляции main.cc не видит определения bar() или каких-либо специализаций, он ожидает, что вызовы bar() будут использовать явные экземпляры неспециализированного определения bar() в некоторой другой единицы перевода. Но так как изменение имени предсказуемо, специализации в foo.cc имеют те же имена символов, что и явные экземпляры неспециализированного определения, поэтому main.cc может использовать эти специализации без их объявления в этой единицы перевода.

Мой вопрос заключается в следующем: это несчастный случай, или это поведение обусловлено стандартом С++? Другими словами, этот переносимый код?

Наиболее актуальным предыдущим вопросом, который я смог найти, является Объявление специалиста по членству в классе шаблонов, но оно не охватывает этот конкретный случай.

(Если вам интересно, почему это имеет значение для меня, это потому, что я использую такой код как своего рода таблицу поиска во время компиляции, и это намного короче, если я не объявляю специализации. )

Ответ 1

Стандарт (С++ 11) требует, чтобы явные специализации были объявлены (но не обязательно определены) до их первого использования:

(14.7.3/6) Если шаблон, шаблон-член или член шаблона класса явно специализирован, то эта специализация должна быть объявлена ​​до первого использования этой специализации, которая вызовет неявное создание экземпляра, в каждой единицы перевода, в которой такое использование происходит; диагностика не требуется. Если программа не дает определения для явной специализации, и либо специализация используется таким образом, чтобы вызвать неявное создание экземпляра, либо член является виртуальной функцией-членом, программа плохо сформирована, не требуется диагностика. Имплицитная инстанция никогда не создается для явной специализации, которая объявлена, но не определена. [...]

Я считаю, что это на практике будет иметь эффект только тогда, когда ваше основное определение шаблона включает определение неспециализированной версии одной из функций-членов. Поскольку в этом случае, когда явная специализация не объявлена, существующее первичное определение может использоваться для компиляции функции inline в код, и специализация в конечном итоге не будет использоваться во время ссылки.

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