Чтобы решить эту проблему, необходимо выполнить вычисления в аргументе по умолчанию для шаблона признака, поскольку попытка изменить определение шаблона нарушает правило ODR (хотя комбинация __COUNTER__
и namespace {}
может обойти ODR).
Это написано на C ++ 11, но может быть настроено для работы в режиме C ++ 03 умеренно недавнего C ++ 11-совместимого компилятора.
template< typename t >
typename std::enable_if< sizeof (t), std::true_type >::type
is_complete_fn( t * );
std::false_type is_complete_fn( ... );
template< typename t, bool value = decltype( is_complete_fn( (t *) nullptr ) )::value >
struct is_complete : std::integral_constant< bool, value > {};
Демоверсия онлайн.
Аргумент по умолчанию оценивается там, где указан шаблон, поэтому он может контекстно переключаться между различными определениями. Нет необходимости в разной специализации и определении при каждом использовании; вам нужен только один для true
и один для false
.
Правило приведено в §8.3.6 / 9, которое в равной степени применяется к аргументам по умолчанию для функции и аргументам шаблона по умолчанию:
Аргументы по умолчанию оцениваются каждый раз, когда вызывается функция.
Но будьте осторожны, использование этого внутри шаблона почти наверняка нарушит ODR. Шаблон, созданный для неполного типа, не должен делать что-либо иначе, чем если бы он был создан для полного типа. Лично я хочу это только для static_assert
.
Кстати, этот принцип также может быть полезен, если вы хотите пойти другим путем и реализовать функциональность __COUNTER__
с использованием шаблонов и перегрузок.