A simple templated 4×4 matrix class using vector intrinsics, thus the need for template specialization to support both integer and floating-point types.
I have the following compiler and flags set:
x86-64 gcc 14.1
- flags:
g++
-std=c++17
-O3
-msse2
- flags:
Here’s the generated error
<code><source>:66:5: error: extra qualification 'Mat4<T>::' on member
'operator+' [-fpermissive]
66 | Mat4<T>::operator+(const Mat4& other) const {
| ^~~~~~~
</code>
<code><source>:66:5: error: extra qualification 'Mat4<T>::' on member
'operator+' [-fpermissive]
66 | Mat4<T>::operator+(const Mat4& other) const {
| ^~~~~~~
</code>
<source>:66:5: error: extra qualification 'Mat4<T>::' on member
'operator+' [-fpermissive]
66 | Mat4<T>::operator+(const Mat4& other) const {
| ^~~~~~~
I narrowed it down to one specific example using the operator+()
overload, one case for integer type and one specialization for float.
Source: sample.cpp
<code>#include <algorithm> // std::clamp
#include <cstdint> // std::uint8_t
#include <immintrin.h> // SSE/SSE2 intrinsics
#include <type_traits> // std::is_floating_point,std::is_integral
constexpr std::uint8_t stride = 4;
constexpr std::uint8_t size = 16;
template <typename T>
struct Mat4 {
T data[size];
// Other operators here:
Mat4 operator+(const Mat4& other) const;
// Other arithmetic operators here:
// Integral types using SSE2
template <typename U>
typename std::enable_if<std::is_integral<U>::value, Mat4<U>>::type
Mat4<T>::operator+(const Mat4& other) const {
Mat4 result;
for (std::uint8_t i = 0; i < size; i += stride) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&data[i]));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&other.data[i]));
__m128i c = _mm_add_epi32(a, b); // Change this to _mm_add_epi64 for 64-bit integers
_mm_storeu_si128(reinterpret_cast<__m128i*>(&result.data[i]), c);
}
return result;
}
// Other Integer Type arithmetic operators here.
};
// Specialization for floating-point types
template <>
Mat4<float> Mat4<float>::operator+(const Mat4& other) const {
Mat4 result;
for (std::uint8_t i = 0; i < size; i += stride) {
__m128 a = _mm_loadu_ps(&data[i]);
__m128 b = _mm_loadu_ps(&other.data[i]);
__m128 c = _mm_add_ps(a, b);
_mm_storeu_ps(&result.data[i], c);
}
return result;
}
// Other floating-point type specialization arithmetic operators here
</code>
<code>#include <algorithm> // std::clamp
#include <cstdint> // std::uint8_t
#include <immintrin.h> // SSE/SSE2 intrinsics
#include <type_traits> // std::is_floating_point,std::is_integral
constexpr std::uint8_t stride = 4;
constexpr std::uint8_t size = 16;
template <typename T>
struct Mat4 {
T data[size];
// Other operators here:
Mat4 operator+(const Mat4& other) const;
// Other arithmetic operators here:
// Integral types using SSE2
template <typename U>
typename std::enable_if<std::is_integral<U>::value, Mat4<U>>::type
Mat4<T>::operator+(const Mat4& other) const {
Mat4 result;
for (std::uint8_t i = 0; i < size; i += stride) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&data[i]));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&other.data[i]));
__m128i c = _mm_add_epi32(a, b); // Change this to _mm_add_epi64 for 64-bit integers
_mm_storeu_si128(reinterpret_cast<__m128i*>(&result.data[i]), c);
}
return result;
}
// Other Integer Type arithmetic operators here.
};
// Specialization for floating-point types
template <>
Mat4<float> Mat4<float>::operator+(const Mat4& other) const {
Mat4 result;
for (std::uint8_t i = 0; i < size; i += stride) {
__m128 a = _mm_loadu_ps(&data[i]);
__m128 b = _mm_loadu_ps(&other.data[i]);
__m128 c = _mm_add_ps(a, b);
_mm_storeu_ps(&result.data[i], c);
}
return result;
}
// Other floating-point type specialization arithmetic operators here
</code>
#include <algorithm> // std::clamp
#include <cstdint> // std::uint8_t
#include <immintrin.h> // SSE/SSE2 intrinsics
#include <type_traits> // std::is_floating_point,std::is_integral
constexpr std::uint8_t stride = 4;
constexpr std::uint8_t size = 16;
template <typename T>
struct Mat4 {
T data[size];
// Other operators here:
Mat4 operator+(const Mat4& other) const;
// Other arithmetic operators here:
// Integral types using SSE2
template <typename U>
typename std::enable_if<std::is_integral<U>::value, Mat4<U>>::type
Mat4<T>::operator+(const Mat4& other) const {
Mat4 result;
for (std::uint8_t i = 0; i < size; i += stride) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&data[i]));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&other.data[i]));
__m128i c = _mm_add_epi32(a, b); // Change this to _mm_add_epi64 for 64-bit integers
_mm_storeu_si128(reinterpret_cast<__m128i*>(&result.data[i]), c);
}
return result;
}
// Other Integer Type arithmetic operators here.
};
// Specialization for floating-point types
template <>
Mat4<float> Mat4<float>::operator+(const Mat4& other) const {
Mat4 result;
for (std::uint8_t i = 0; i < size; i += stride) {
__m128 a = _mm_loadu_ps(&data[i]);
__m128 b = _mm_loadu_ps(&other.data[i]);
__m128 c = _mm_add_ps(a, b);
_mm_storeu_ps(&result.data[i], c);
}
return result;
}
// Other floating-point type specialization arithmetic operators here
I even attempted to switch the compiler to MSVC
and was getting a similar compiler error but still no success, and eventually I reverted back to GCC
.
12