C++ - определение функции-члена вне шаблона-класса, но в заголовке

Я определил простой шаблон класса с одной функцией-членом. Он определен вне класса с дополнительной (явной) специализацией, также определенной вне класса. Все в одном заголовочном файле. Если вы включите этот заголовок в несколько блоков перевода, вы получите ошибку компоновщика из-за One-Definition-Rule.

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction();
};

template <class T>
bool TestClass<T>::MemberFunction()
{
    return true;
}

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

Пока все хорошо. Но если я помещу определение функции-члена в тело класса, ошибка компоновщика исчезнет, ​​и функции можно будет использовать в разных единицах перевода.

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction()
    {
        return true;
    }
};

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

Мой вопрос: почему это так работает? Я использую MSVC 2012. У ODR есть некоторые исключения для шаблонов, что я сначала и назвал причиной. Но определение "базовой" функции внутри / вне класса имеет значение здесь.

1 ответ

Решение

14,7/5 говорит

5 Для заданного шаблона и заданного набора аргументов шаблона,

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

Реализация не требуется для диагностики нарушения этого правила.

Вторая пуля относится к вашему делу. ODR, определенный в 3.2, говорит то же самое, хотя и в менее дистиллированной форме.

Независимо от того, где и как определяется неспециализированная версия функции-члена, определение специализированной версии

template <> bool TestClass<double>::MemberFunction()
{
    return true;
};

должен идти в .cpp файл. Если он хранится в заголовочном файле, он вызовет нарушение ODR, как только заголовок будет включен в несколько блоков перевода. GCC достоверно обнаружит это нарушение. MSVC кажется менее надежным в этом отношении. Но, как указано в приведенной выше цитате, реализация не требуется для диагностики нарушения этого правила.

Заголовочный файл должен содержать только неопределяющее объявление этой специализации

template <> bool TestClass<double>::MemberFunction();

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


После дальнейших исследований выясняется, что реализация MSVC фактически нарушена: ее поведение выходит за рамки того, что разрешено разрешением "не требуется никакой диагностики", заданным в спецификации языка.

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

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

Другие вопросы по тегам