error: recursive template instantiation exceeded maximum depth
А всё из-за того, что из Bits<T, 0> идёт бесконечно рекурсивный вызов к Bits<T, 0>. Если бы это были обычные функции, то такой рекурсивный вызов не происходил бы из-за условия в тернарном операторе, но для шаблонов соответствующую ветку компилятор всё равно обязан рассмотреть.
Ш>Теперь некоторые вещи можно упростить.
Вот сначала приведи в соответствие со стандартом предыдущий код, а потом сравни простоту и понятность с тем же
std::numeric_limits<UInt>::digits
Ну или с вариантом, завёрнутым в свой шаблон с аналогичным твоему именем:
template <class UInt> constexpr auto BitsOf = std::numeric_limits<UInt>::digits;
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, Шахтер, Вы писали:
Ш>>Считаем число бит в беззнаковом типе.
W>Не считает:
error: recursive template instantiation exceeded maximum depth
А всё из-за того, что из Bits<T, 0> идёт бесконечно рекурсивный вызов к Bits<T, 0>. Если бы это были обычные функции, то такой рекурсивный вызов не происходил бы из-за условия в тернарном операторе, но для шаблонов соответствующую ветку компилятор всё равно обязан рассмотреть.
У меня считает.
Ш>>Теперь некоторые вещи можно упростить. W>Вот сначала приведи в соответствие со стандартом предыдущий код, а потом сравни простоту и понятность с тем же
template <class UInt> constexpr auto BitsOf = std::numeric_limits<UInt>::digits;
error: recursive template instantiation exceeded maximum depth
А всё из-за того, что из Bits<T, 0> идёт бесконечно рекурсивный вызов к Bits<T, 0>. Если бы это были обычные функции, то такой рекурсивный вызов не происходил бы из-за условия в тернарном операторе, но для шаблонов соответствующую ветку компилятор всё равно обязан рассмотреть.
Ну так, может быть, просто разные компилеры используются?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, LaptevVV, Вы писали:
LVV>Ну так, может быть, просто разные компилеры используются?
Конечно разные. Но смысл не в том, что они разные, а в том, что результат шаблона из первого сообщения неопределён. И поэтому такой код не нужно писать или использовать. То что разные компиляторы выдают разные ответы — лишь следствие проблемы в исходном тексте.
Ну и если уж разговор зашёл об компиляторах, то, например, gcc-5.2 компилирует исходный пример, а clang-3.7 выдаёт ошибку. С другой стороны, если пример чуть-чуть переписать пользуясь средствами только C++03, то ситуация может изменится на противоположную: gcc-5.2 теперь находит бесконечную рекурсию, а clang-3.7 выдаёт ответ. Так что тут даже нельзя утверждать что gcc или clang хуже другого справляются с таким кодом — оба иногда сообщают об ошибке (что хорошо), а иногда выдают численный ответ (что на самом деле хуже, так как может замаскировать ошибки в более сложных случаях).
Ну и если уж выбирать способ считать биты, то куда лучше вообще вместо шаблонов взять constexpr-функции — в них правила рекурсии другие, более интуитивно понятные и с ними всё работает во всех компиляторах, удовлетворяющих стандарту:
ну а теперь представь себе 80-битовое число (например, с плавающей точкой), которое архитектура требует выравнивать по 16-байтовым границам.
sizeof такого числа будет 16, а не 10.
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, LaptevVV, Вы писали:
LVV>>Ну так, может быть, просто разные компилеры используются?
W>Конечно разные. Но смысл не в том, что они разные, а в том, что результат шаблона из первого сообщения неопределён.
A>есть же constexpr функции, зачем писать такую нечитаемую штуку
Ну не знаю, я вполне себе читаю.
А constexpr-функции использовать тоже можно, но тогда придётся делать две декларации -- сначала функцию, а потом константу.
Здравствуйте, Шахтер, Вы писали:
Ш>Считаем число бит в беззнаковом типе.
В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).
Здравствуйте, ArtDenis, Вы писали:
AD>В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).
В любом случае это не будет работать для знаковых типов потому как "Val>>1" немного не определен
Если хотим поддерживать и знаковые числа нужно вызывать std::make_unsigned .
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, Шахтер, Вы писали:
Ш>>Считаем число бит в беззнаковом типе.
AD>В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).
Эта конструкция только для беззнаковых чисел. Да и речь не о том. Подсчет числа битов взят только как пример. Речь о рекурсии.
Здравствуйте, ArtDenis, Вы писали:
Ш>>Считаем число бит в беззнаковом типе.
AD>В дополнении к другим придиркам тоже добавлю что Val=UInt(-1) лучше заменить Val=UInt(~0). Так "более" правильно, т.к. представление отрицательных чисел может быть разным (в случае представления под названием "код со сдвигом" твой код будет выдавать некорректный результат).
А чем Val=UInt(~0) лучше Val=UInt(-1)?
UInt(-1) гарантированно даст наибольшее целое влезающие в беззнаковый UInt. А разве можно сказать то же про UInt(~0)?
Здравствуйте, B0FEE664, Вы писали:
BFE>UInt(-1) гарантированно даст наибольшее целое влезающие в беззнаковый UInt.
С какой это стати?
BFE>А разве можно сказать то же про UInt(~0)?
Сложно сказать. Надо посмотреть что прописано в стандарте насчёт каста (int)~0 к типу unsigned long long. Возможно что и ~0 тоже не катит для данного случая.
Здравствуйте, ArtDenis, Вы писали:
BFE>>UInt(-1) гарантированно даст наибольшее целое влезающие в беззнаковый UInt. AD>С какой это стати?
Следствие из 4.7/2 стандарта.
BFE>>А разве можно сказать то же про UInt(~0)? AD>Сложно сказать.
Вот именно.
AD>Надо посмотреть что прописано в стандарте насчёт каста (int)~0 к типу unsigned long long. Возможно что и ~0 тоже не катит для данного случая.
Прежде чем к касту переходить, надо выяснить что даст ~0.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Шахтер, Вы писали:
Ш>>Теперь некоторые вещи можно упростить.
К>Грабельки. Не надо такое тащить в продакшен.
К>http://ideone.com/a83SVo К>
К>#include <iostream>
К>using namespace std;
К>template<int X> int const num = X==1 ? 0 : 1 + num<X%2 ? X*3+1 : X/2>;
К>//template<int X> int const ccc = X==0 ? 0 : 1 + ccc<0>; // ошибка: константа ccc<0> явно определена через саму себя
К>template<int X> int const rec = X==0 ? 0 : 1 + rec<(X ? 0 : 0)>; // поэтому прибегнем к трюку, сделаем вид, что параметр вычисляется
К>template<int X> int const err = X==0 ? 0 : 1 + err<(X ? X : X)>;
К>template<int X> int constexpr eee = X==0 ? 0 : 1 + eee<(X ? X : X)>;
К>template<int X> int const inf = X==0 ? 0 : 1+inf<X-1>;
К>int n[num<27>];
К>int r[rec<10>];
К>//int e[err<10>]; // это не константа времени компиляции, а статическая переменная
К>//int e[eee<10>]; // зацикливание в constexpr
К>int main() {
К> cout << num<27> << endl; // 111 - честно вычислил рекурсию
К> cout << rec<10> << endl; // 1 - честно вычислил рекурсию
К> cout << err<10> << endl; // 1 - особенности инициализации статической переменной
К>//cout << eee<10> << endl; // ошибка: зацикливание в constexpr
К>//cout << inf<10> << endl; // ошибка: исчерпание глубины рекурсии, inf<-899>
К>}
К>
Скобки забыл поставить в тернарном операторе. Это так и было задумано?
В целом я так и не понял, чего ты добивался.