Затем прямо в определении класса пытаюсь его инстанцировать:
template short func <short, short> (short);
На что MS VC++ версий от 13.10 до 15.00 выдает "error C2252: cannot explicitly instantiate template in current scope". В 19.10 сообщение поменялось на "error C2252: an explicit instantiation of a template can only occur at namespace scope".
В описании ошибки предлагается добавить "<>" после template, и заодно там явно указывается имя класса.
Вот такой пример успешно компилируется всеми имеющимися у меня версиями MS VC++, которые дружно оформляют все вызовы func, как внешние. То есть, фактически не срабатывают ни инстанциаци, ни инлайнинг.
Скрытый текст
class Calc {
int i;
public:
template <typename ArgType, typename ResType>
ResType func (ArgType Arg) { return ResType (Arg * i); }
template <> short func <short, short> (short);
template <> unsigned int func <unsigned int, unsigned int> (unsigned int);
};
int f (short s, unsigned int ui) {
Calc c;
short s2 = c.func <short, short> (s);
unsigned int ui2 = c.func <unsigned int, unsigned int> (ui);
return int (s2 + ui2);
}
int main () {
return f (-31435, 8757849873);
}
Что там нужно сделать, чтобы нужные версии функции явно инстанциировались и проинлайнились в режиме оптимизации?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>На что MS VC++ версий от 13.10 до 15.00 выдает "error C2252: cannot explicitly instantiate template in current scope". В 19.10 сообщение поменялось на "error C2252: an explicit instantiation of a template can only occur at namespace scope".
Согласно стандару (п. 17.7.2/6), такая явная специализация должна выполняться в обрамляющем пространстве имен, о чем компилятор и говорит вполне ясно.
An explicit instantiation of a class, function template, or variable template specialization is placed in the namespace in which the template is defined. An explicit instantiation for a member of a class template is placed in the namespace where the enclosing class template is defined. An explicit instantiation for a member template is placed in the namespace where the enclosing class or class template is defined.
ЕМ>В описании ошибки предлагается добавить "<>" после template, и заодно там явно указывается имя класса.
Пардон за каламбур, но похоже, в опсании ошибки допущена ошибка — явную специализацию хотели сделать как положено, в пространстве имен (о чем намекает присутствие явной спецификации имени класса), но нечаянно загнали внутрь класса.
ЕМ>Вот такой пример успешно компилируется всеми имеющимися у меня версиями MS VC++...
Считай это ошибкой прошлого
--
Re: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Хочу в классе иметь несколько вариантов одной и той же мелкой вычислительной (а потому инлайновой) функции. Создаю шаблон:
ЕМ>
ЕМ>Затем прямо в определении класса пытаюсь его инстанцировать:
Но зачем?!
Убери свои строки с "инстанциированием" из исходника — и всё будет работать как надо.
ЕМ>В описании ошибки предлагается добавить "<>" после template, и заодно там явно указывается имя класса.
Забавно :)
Добавление <> — это для описания специализации.
То есть, для ситуации, когда кто-то хотел сделать специализацию, но забыл написать <>, в справке говорят, что нужно написать <>.
ЕМ>Вот такой пример успешно компилируется всеми имеющимися у меня версиями MS VC++, которые дружно оформляют все вызовы func, как внешние. То есть, фактически не срабатывают ни инстанциаци, ни инлайнинг.
Ну в некоторой степени это даже логично: ты добавил <> и тем самым написал, что будет специализация. Но тело функции для специализации не предоставил. Разумеется компилятор тут ничего кроме внешнего вызова и не может сделать.
ЕМ>Что там нужно сделать, чтобы нужные версии функции явно инстанциировались и проинлайнились в режиме оптимизации?
Инстанциирование делается в namespace-scope:
class Calc { … };
template short Calc::func <short, short> (short z);
Но повторю: не надо это делать явно. Ну или объясняй зачем.
Здравствуйте, Евгений Музыченко, Вы писали:
R>>Согласно стандару (п. 17.7.2/6), такая явная специализация должна выполняться в обрамляющем пространстве имен
ЕМ>Я это подозревал, и пробовал выносить наружу, но там возникали какие-то другие ошибки.
С точки зрения стандарта валидной является любая из следующих форм:
template short A::func (short);
template short A::func<> (short);
template short A::func<short> (short);
template short A::func<short, short> (short);
Попробуй так и эдак, какая-нибудь да подойдет.
И еще, я бы на твоем месте поменял параметры шаблона местами. Посколько тип параметра выводится автоматически, а вот тип результата всегда придется указывать явно.
--
Re[2]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, watchmaker, Вы писали:
ЕМ>>Затем прямо в определении класса пытаюсь его инстанцировать:
W>Но зачем?!
В исходном варианте некоторых функций были принудительные приведения типов для обрезания ненужных старших частей 64-разрядных результатов, чтобы вернуть только значащую 32-разрядную часть, и избежать лишних расходов на 32-разрядных платформах. Поэтому хотелось инстанцировать только безопасные сочетания типов. Сейчас уже кажется, что правильнее будет явно указывать типы при вызовах таких функций.
W>Добавление <> — это для описания специализации.
Стыдно сказать, но я до сих пор не до конца понимаю, чем отличается специализация шаблона от инстанцирования. Я на C++ делаю в основном низкоуровневный код, поэтому никогда предметно не разбирался с конструкциями, способными давать трудно контролируемый рост кода. Поэтому из шаблонов применяю в первую очередь шаблоны классов, экземпляры которых сами не плодятся.
Re[4]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Поэтому хотелось инстанцировать только безопасные сочетания типов.
Иначе говоря, ты вручную явно инстанциируешь все безопасные комбинации, а компилятору оставишь работу по неявному инстанциированию всех остальных вариантов? Отличный план!
Ну то есть инстанциирование — это про другое.
Если хочешь запретить некие комбинации типов, то используй enable_if или просто static_assert с условием напиши.
Если хочешь для некоторых комбинаций типов сделать специальную логику обработки (не по шаблону), то используй специализацию этих типов для них (даже слова однокоренные!).
Re[4]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, watchmaker, Вы писали:
W>Иначе говоря, ты вручную явно инстанциируешь все безопасные комбинации, а компилятору оставишь работу по неявному инстанциированию всех остальных вариантов?
Насколько я понимаю, так у него будет меньше шансов автоматически выбрать опасный вариант.
W>Если хочешь запретить некие комбинации типов, то используй enable_if или просто static_assert с условием напиши.
Я в коде для ядра не использую std, чтоб не возникало неожиданных зависимостей. Попробую выдрать из библиотеки.
Re[5]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Евгений Музыченко, Вы писали:
W>>Иначе говоря, ты вручную явно инстанциируешь все безопасные комбинации, а компилятору оставишь работу по неявному инстанциированию всех остальных вариантов?
ЕМ>Насколько я понимаю, так у него будет меньше шансов автоматически выбрать опасный вариант.
Нет. Ты меняешь время и место инстанцирования, только и всего. На выбор специализаций/перегрузок это никак не влияет.
--
Re: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Затем прямо в определении класса пытаюсь его инстанцировать:
А зачем тебе вообще шблон в этом месте? Просто перегруженный метод напиши, и не парься.
С другой стороны, тип результата всё равно явно надо указывать, при вызове, так что не ясно чем шаблон плох. Зачем с ним бороться и пытаться какие-то комбинации запретить?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Erop, Вы писали:
E>А зачем тебе вообще шблон в этом месте? Просто перегруженный метод напиши, и не парься.
Получается слишком громоздко для всех возможных типов параметров и результатов. Например, группа функций преобразования свойств звуковых потоков: миллисекунды в кадры, миллисекунды в байты, микросекунды в байты, и все это обратно. Каждый вариант может принимать и возвращать 32- и 64-разрядные значения.
Можно, конечно, вычислять все в 64-разрядной точности, а затем обрезать, если нужно, но на 32-разрядных платформах это слишком расточительно в отношении небольших объемов/длительностей, которые отлично вычисляются в 32 разрядах.
E>Зачем с ним бороться и пытаться какие-то комбинации запретить?
Да просто для вящей безопасности, чтобы не обрезать лишнего.
Кстати, тут еще одна проблема — при повышении разрядности от параметров к результату хочется как-то обеспечить автоматическое повышение точности. Например, в шаблоне
template <typename ResType, typename ArgType> ResType MulDiv (ArgType a, ArgType b) {
return static_cast <ResType> (a * b / 1000000);
}
легко можно вызвать промежуточное переполнение и потерю старших разрядов, если параметры 32-разрядные, а результат — 64-разрядный.
Есть ли более надежные способы борьбы с этим, кроме вот такого извращения?
return static_cast <ResType> (static_cast <ResType> (0) + a * b / 1000000);
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Кстати, тут еще одна проблема — при повышении разрядности от параметров к результату хочется как-то обеспечить автоматическое повышение точности. Например, в шаблоне
ЕМ>
ЕМ>template <typename ResType, typename ArgType> ResType MulDiv (ArgType a, ArgType b) {
ЕМ> return static_cast <ResType> (a * b / 1000000);
ЕМ>}
ЕМ>
ЕМ>легко можно вызвать промежуточное переполнение и потерю старших разрядов, если параметры 32-разрядные, а результат — 64-разрядный.
ЕМ>Есть ли более надежные способы борьбы с этим, кроме вот такого извращения?
Здравствуйте, andyp, Вы писали:
A>я бы ResultType и тип для промежуточных вычислений выбирал как-то так:
Я бы с удовольствием, но у меня много кода под виндовое ядро, там std [почти] нет, а выдирать и тащить фрагментами — то еще удовольствие. Ну и очень желательно сделать все средствами VC++ 15.00 (из VS 2008 и WDK 7).
Re[3]: Не могу явно инстанцировать шаблон функции-члена
ЕМ>template <typename ResType, typename ArgType> ResType MulDiv (ArgType a, ArgType b) {
ЕМ> return static_cast <ResType> (a * b / 1000000);
ЕМ>}
ЕМ>легко можно вызвать промежуточное переполнение и потерю старших разрядов, если параметры 32-разрядные, а результат — 64-разрядный. ЕМ>Есть ли более надежные способы борьбы с этим, кроме вот такого извращения?
ЕМ> return static_cast <ResType> (static_cast <ResType> (0) + a * b / 1000000);
Надеюсь, что этот пример был взят не из реального кода, а просто невнимательно написан прямо на форуме.
Ибо такая запись не только выглядит некрасиво, но ещё и оказывается семантически эквивалентной первому варианту. То есть тоже будет страдать от 32-х битного переполнения. Ведь хотя паттерн static_cast<ResType>(0) действительно существует, но в данном коде он использован неправильно.
Re[5]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Я бы с удовольствием, но у меня много кода под виндовое ядро, там std [почти] нет, а выдирать и тащить фрагментами — то еще удовольствие. Ну и очень желательно сделать все средствами VC++ 15.00 (из VS 2008 и WDK 7).
Я про ядро и возможности VS2008 мало знаю (.
Сухая идея — избавиться от второго параметра шаблона функции выводя тип результата из типа аргумента функции, используя трейты для нужных типов. Всё, что использовал, живет в cstdint и type_traits и может быть использовано без С++11, если использовать enable_if<...>::type вместо enable_if_t и typedef вместо using.
Если совсем уж без стандартных заголовков, то примерно вот велосипедные is_same и enable_if:
template<typename First, typename Second> is_same{static const bool value = false;};
temaple<typename T> is_same<T, T>{static const bool value = true;}
template<bool Key, class T = void> struct enable_if{};
template<class T> struct enable_if<true, T> {typedef T type;};
Re[4]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, Евгений Музыченко, Вы писали:
W>>Если хочешь запретить некие комбинации типов, то используй enable_if или просто static_assert с условием напиши. ЕМ>Я в коде для ядра не использую std, чтоб не возникало неожиданных зависимостей. Попробую выдрать из библиотеки.
В свежей студии (VS 2017 15.8) можно уже спокойно использовать <type_traits>, они специально для драйверистов его почистили: https://blogs.msdn.microsoft.com/vcblog/2018/09/18/stl-features-and-fixes-in-vs-2017-15-8/
Re[6]: Не могу явно инстанцировать шаблон функции-члена
Здравствуйте, andyp, Вы писали:
A>Я про ядро и возможности VS2008 мало знаю (.
Использование стандартных шаблонов само по себе никак не мешает компиляции под ядро, ибо все разруливается компилятором, но включение type_traits тянет за собой заголовки, несовместимые с заголовками WDK/DDK. Поэтому только выдирать, или определять собственное.
A>Сухая идея — избавиться от второго параметра шаблона функции выводя тип результата из типа аргумента функции, используя трейты для нужных типов.
Мне кажется, что для функций с тремя-четырьмя параметрами это будет неимоверно громоздко. Например, у меня есть функция MulAddDiv, вычисляющая выражение "(a * b + c) / d" с использованием особенностей x86 (команды перемножения 32-разрядных в 64-разрядное, и обратный вариант деления). Соответственно, для 32-разрядных типов используется эта реализация, а все остальные варианты, имеющие хотя бы один 64-разрядный параметр, реализуются "втупую".
Но из-за ублюдочного подхода C++, не позволяющего явно указать приоритеты неявных преобразований, мне приходится явно определять все возможные 32-разрядные варианты (int/long, signed/unsigned). И это я еще не использую эти функции с char и short. Остальные варианты можно определить шаблонами, но и тут элементарная, по сути, задача превращается в танцы с бубном и сотню строк кода.
A>Если совсем уж без стандартных заголовков, то примерно вот велосипедные is_same и enable_if: