I am using clangd as LSP, with C++20 and I have stuck with such a problem. I have no idea why I cannot initialize a constexpr array with a constexpr function. Inserting a direct values works but I would like to understand why my "automatic" solution doesn’t work.
Error returned by clangd LSP is probably because of a second issue, but I don’t know why defined function is not seen as defined.
IpRange.h
class IpRange {
inline static constexpr uint32_t getMaskValue(const ushort shortForm) {
return 2^(32 - shortForm) - 1;
}
template <typename T>
inline static constexpr T getMaskValues() {
auto masks = T();
for (ushort i = 0; i < 33 ; i++) {
masks[i] = getMaskValue(i);
}
return masks;
}
static constexpr std::array<uint32_t, 33> maskValues { getMaskValues<std::array<uint32_t, 33>>() };
//Returns clangd error: Constexpr variable 'maskValues' must be initialized by a constant expression
//Returns compilation error: constexpr T IpRange::getMaskValues() [with T = std::array<unsigned int, 33>]’ used before its definition
static constexpr std::array<uint32_t, 33> maskValues { 1, 2, 3 };
//Works fine...
}
Solution. Moving Masks-stuff to another class.
#define MASK_VAL_NUM 33
class MaskValues {
typedef std::array<uint32_t, MASK_VAL_NUM> MaskValA;
inline static constexpr uint32_t getMaskValue(const ushort shortForm) {
return 2^(32 - shortForm) - 1;
}
inline static constexpr MaskValA getMaskValues() {
auto masks = MaskValA();
for (ushort i = 0; i < MASK_VAL_NUM ; i++) {
masks[i] = getMaskValue(i);
}
return masks;
}
const std::array<uint32_t, MASK_VAL_NUM> maskValues;
public:
constexpr MaskValues(): maskValues{getMaskValues()} {
}
const uint32_t& operator[](std::size_t idx) const {
return this->maskValues[idx];
}
};
class IpRange {
static constexpr MaskValues maskValues {};
}
>Solution :
G++ gives a better error message for this one than Clang:
main.cpp:19:99: error: ‘static constexpr T IpRange::getMaskValues() [with T = std::array<unsigned int, 33>]’ used before its definition
19 | es<std::array<uint32_t, 33>>();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
The problem is that you’re calling the getMaskValues
while the definition of the class is not yet complete and you can’t call a member function of a class that is in this state.
Even if you remove the templates, you will still get this error message from GCC:
main.cpp:18:73: error: ‘static constexpr std::array<unsigned int, 33> IpRange::getMaskValues()’ called in a constant expression before its definition is complete
18 | static constexpr std::array<uint32_t, 33> maskValues = getMaskValues();
| ~~~~~~~~~~~~~^~
Essentially you can’t call IpRange::getMaskValues
while IpRange
is still not complete.
To fix this:
- You can move
getMaskValues
(and whatever it needs) into a separate class (moving into sub-classes won’t work) or define them globally - You can make the
IpRange
class a templated class.