При инстанцировании шаблонного класса когда ошибка в теле его функции-члена становится ошибкой компиляции?
Для зависимого тела функции-члена ошибка обычно обнаруживается не при создании специализации класса, а при необходимости инстанцировать саму функцию-член — например, при её вызове. Поэтому специализация класса может быть корректной, пока проблемный метод не используется.
Отложенная инстанциация позволяет шаблонному классу содержать несколько универсальных операций, из которых конкретному типу нужны не все. Компилятор не обязан формировать и проверять определения всех функций-членов для каждой специализации, если они не требуются программе.
Такой подход уменьшает лишнюю работу компилятора и позволяет писать обобщённые типы с операциями, применимыми только к части возможных параметров. При этом ошибки в независимой части шаблона по-прежнему диагностируются раньше — уже при определении шаблона.
Важно различать инстанцирование специализации класса и инстанцирование отдельной функции-члена. Неверное ожидание, что создание объекта немедленно проверит все методы класса, приводит либо к ошибочному отказу от допустимого шаблона, либо к удивлению из-за поздней диагностики.
Например, класс может быть корректно создан для типа без операции сложения, если метод, использующий сложение, не вызывается. Если же этот метод становится нужным, компилятор должен сформировать его тело и сообщить об отсутствии требуемой операции.
При неявном инстанцировании шаблонного класса компилятор формирует его структуру и проверяет необходимые декларации. Определения обычных не виртуальных функций-членов, которые не используются, как правило, не инстанцируются только из-за создания специализации класса.
В этом примере Box<int> корректен, а print() не формируется, поскольку не вызывается. Если добавить вызов b.print(), инстанцирование тела обнаружит, что у int нет метода print.
Есть важные ограничения. Некорректная не зависящая от параметра часть тела проверяется уже при определении шаблона. Кроме того, виртуальная функция может потребоваться для формирования таблицы виртуальных функций, поэтому рассчитывать на полную отсрочку её проверки безоговорочно нельзя.
Если требование к типу должно диагностироваться на границе API, лучше выразить его через концепт, requires или другой механизм ограничения шаблона. Тогда неподходящий тип будет исключён или отклонён раньше и с более понятным сообщением, вместо поздней ошибки внутри тела метода.
В библиотечном контейнере есть метод сериализации, использующий операции потокового вывода, и методы доступа к размеру. Контейнер требуется использовать с типами, которые можно хранить и измерять, но не обязательно сериализовать.
Первый вариант — оставить проверку только в теле метода сериализации. Его плюс — простая реализация и возможность не требовать сериализацию для остальных операций. Минус — ошибка появляется только при вызове метода и может быть плохо связана с контрактом API.
Второй вариант — ограничить весь класс требованием сериализуемости. Это даёт раннюю диагностику, но чрезмерно сужает область применения: типы, которым сериализация не нужна, больше нельзя использовать.
Практичнее ограничить только метод сериализации. Тогда класс инстанцируется для более широкого набора типов, а вызов недоступной операции получает явную диагностику в месте использования. Отложенная инстанциация сохраняется, но контракт конкретного метода становится формальным и проверяемым.
Нет. SFINAE действует главным образом на этапе подстановки в непосредственном контексте объявления и параметров шаблона. Ошибка в уже выбранном и инстанцируемом теле функции обычно является настоящей ошибкой компиляции, а не поводом тихо исключить перегрузку.
Обычно да, если операция требует проверки конкретного определения или формирования соответствующего адреса. Само наличие имени класса ещё не означает использования всех его членов, но выражение, обращающееся к проблемной функции-члену, может сделать её инстанцирование необходимым.
Нет. Независимые ошибки проверяются при разборе самого шаблона, а некоторые члены могут потребоваться компилятору для формирования виртуальной таблицы, константного выражения или других структур программы. Поэтому точнее говорить: зависимая часть обычной неиспользуемой функции-члена обычно откладывается, но это не универсальная гарантия для любого члена и любого контекста.