Параметризация и «частичная специализация шаблона функции не допускаются»

Это продолжение Что является эквивалентом параметра функции constexpr? В первоначальном вопросе мы пытаемся ускорить некоторый код, который выполняет сдвиги и вращения под Clang и VC ++. Clang и VC ++ плохо оптимизируют код, потому что он обрабатывает величину сдвига / поворота как переменную (т.е. не constexpr).

Когда я пытаюсь параметризовать величину сдвига и размер слова, это приводит к:

$ g++ -std=c++11 -march=native test.cxx -o test.exe
test.cxx:13:10: error: function template partial specialization is not allowed
uint32_t LeftRotate<uint32_t, unsigned int>(uint32_t v)
^         ~~~~~~~~~~~~~~~~~~~~~~~~
test.cxx:21:10: error: function template partial specialization is not allowed
uint64_t LeftRotate<uint64_t, unsigned int>(uint64_t v)
^         ~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

Вот тестовая программа. Это немного больше, чем нужно, поэтому люди могут видеть, что нам нужно справиться с обоими uint32_t а также uint64_t (не говоря уже о uint8_t, uint16_t и другие виды).

$ cat test.cxx
#include <iostream>
#include <stdint.h>

template<typename T, unsigned int R>
inline T LeftRotate(unsigned int v)
{
static const unsigned int THIS_SIZE = sizeof(T)*8;
static const unsigned int MASK = THIS_SIZE-1;
return T((v<<R)|(v>>(-R&MASK)));
};

template<uint32_t, unsigned int R>
uint32_t LeftRotate<uint32_t, unsigned int>(uint32_t v)
{
__asm__ ("roll %1, %0" : "+mq" (v) : "I" ((unsigned char)R));
return v;
}

#if __x86_64__
template<uint64_t, unsigned int R>
uint64_t LeftRotate<uint64_t, unsigned int>(uint64_t v)
{
__asm__ ("rolq %1, %0" : "+mq" (v) : "J" ((unsigned char)R));
return v;
}
#endif

int main(int argc, char* argv[])
{
std::cout << "Rotated: " << LeftRotate<uint32_t, 2>((uint32_t)argc) << std::endl;
return 0;
}

Я прошел через несколько итераций сообщений об ошибках в зависимости от того, как я пытаюсь реализовать вращение. Другие сообщения об ошибках включают no function template matches function template specialization.... С помощью template <> кажется, производит самый непонятный.

Как я могу параметризовать величину сдвига в надежде, что Clang и VC ++ оптимизируют вызов функции, как и ожидалось?

3

Решение

Другой способ — превратить шаблонную константу в постоянный аргумент, который компилятор может оптимизировать.

Шаг 1: определить концепцию rotate_distance:

template<unsigned int R> using rotate_distance = std::integral_constant<unsigned int, R>;

Шаг 2: определить функции поворота в терминах перегрузок функции, которая принимает аргумент этого типа:

template<unsigned int R>
uint32_t LeftRotate(uint32_t v, rotate_distance<R>)

Теперь, если мы хотим, мы можем просто позвонить LeftRotate(x, rotate_distance<y>())который, кажется, выражает намерение хорошо,

или теперь мы можем переопределить форму шаблона с двумя аргументами в терминах этой формы:

template<unsigned int Dist, class T>
T LeftRotate(T t)
{
return LeftRotate(t, rotate_distance<Dist>());
}

Полная демонстрация:

#include <iostream>
#include <stdint.h>
#include <utility>

template<unsigned int R> using rotate_distance = std::integral_constant<unsigned int, R>;

template<typename T, unsigned int R>
inline T LeftRotate(unsigned int v, rotate_distance<R>)
{
static const unsigned int THIS_SIZE = sizeof(T)*8;
static const unsigned int MASK = THIS_SIZE-1;
return T((v<<R)|(v>>(-R&MASK)));
}

template<unsigned int R>
uint32_t LeftRotate(uint32_t v, rotate_distance<R>)
{
__asm__ ("roll %1, %0" : "+mq" (v) : "I" ((unsigned char)R));
return v;
}

#if __x86_64__
template<unsigned int R>
uint64_t LeftRotate(uint64_t v, rotate_distance<R>)
{
__asm__ ("rolq %1, %0" : "+mq" (v) : "J" ((unsigned char)R));
return v;
}
#endiftemplate<unsigned int Dist, class T>
T LeftRotate(T t)
{
return LeftRotate(t, rotate_distance<Dist>());
}

int main(int argc, char* argv[])
{
std::cout << "Rotated: " << LeftRotate((uint32_t)argc, rotate_distance<2>()) << std::endl;
std::cout << "Rotated: " << LeftRotate((uint64_t)argc, rotate_distance<2>()) << std::endl;
std::cout << "Rotated: " << LeftRotate<2>((uint64_t)argc) << std::endl;
return 0;
}

до-++ 11 компиляторы

До c ++ 11 у нас не было std :: integra_constant, поэтому мы должны сделать свою собственную версию.

Для наших целей этого достаточно:

template<unsigned int R> struct rotate_distance {};

полное доказательство — обратите внимание на эффект оптимизации:

https://godbolt.org/g/p4tsQ5

2

Другие решения

Используйте шаблонный класс, а не шаблонную функцию:

#include <iostream>
#include <stdint.h>template<typename T, unsigned int R>
struct LeftRotate {
static inline T compute(T v)
{
static const unsigned int THIS_SIZE = sizeof(T)*8;
static const unsigned int MASK = THIS_SIZE-1;
return T((v<<R)|(v>>(-R&MASK)));
}
};template<unsigned int R>
struct LeftRotate<uint32_t, R> {
static inline uint32_t compute(uint32_t v)
{
__asm__ ("roll %1, %0" : "+mq" (v) : "I" ((unsigned char)R));
return v;
}
};

#if __x86_64__
template<unsigned int R>
struct LeftRotate<uint64_t, R> {
static inline uint64_t compute(uint64_t v)
{
__asm__ ("rolq %1, %0" : "+mq" (v) : "J" ((unsigned char)R));
return v;
}
};
#endif

int main(int argc, char* argv[])
{
std::cout << "Rotated: " << LeftRotate<uint32_t, 2>::compute((uint32_t)argc) << std::endl;
return 0;
}
1

По вопросам рекламы [email protected]