I have a class that wraps an array. It inherits from an abstract base class defining one virtual constexpr
method for the function-call operator. In the child class, I override said method and access the internal array:
#include <cstddef>
#include <array>
#include <initializer_list>
template <typename T, std::size_t N>
class ContainerBase {
public:
virtual constexpr const T& operator()(std::size_t i) const = 0;
};
template <typename T, std::size_t N>
class Container : public ContainerBase<T, N> {
public:
constexpr Container(std::initializer_list<T> data) {
std::copy(data.begin(), data.end(), _items.begin());
}
constexpr const T& operator()(std::size_t i) const override {
return _items[i];
}
private:
std::array<T, N> _items;
};
int main () {
constexpr Container<int, 3> C = {2, -91, 7};
constexpr int F = C(1);
static_assert(F == -91);
}
Here is the godbolt link.
To the best of my understanding, this is all legal code in C 20, which allows virtual constexpr
. G 10.3 and Clang 12 both accept this as valid code, however MSVC 19.33 does not accept it, claiming that variable F
is not a constant expression:
msvc_buggy_constexpr.cpp(29,21): error C2131: expression did not evaluate to a constant
msvc_buggy_constexpr.cpp(21,16): message : a non-constant (sub-)expression was encountered
msvc_buggy_constexpr.cpp(31,5): error C2131: expression did not evaluate to a constant
msvc_buggy_constexpr.cpp(21,16): message : a non-constant (sub-)expression was encountered
What gives? This looks like a compiler bug in MSVC to me. I would investigate further but MSVC on godbolt.org seems to be down right now.
I should add, the issue only presents itself when the function call operator method is virtual
—when it is not, the issue does not occur.
Can anyone advise?
CodePudding user response:
User @Barry agrees with me that it's definitely a bug in MSVC.
I've submitted this bug report.
Hopefully the issue gets resolved soon.
Thanks all for your comments and further insights, it's very helpful!
CodePudding user response:
I'm not really sure why putting _items
in the base class will make MSVC accepts the code, maybe the virtual overloaded operator()
without _items
being in the base class make MSVC somehow thinks that it's not qualified for constexpr.
template <typename T, std::size_t N>
class ContainerBase
{
protected:
std::array<T, N> _items;
public:
constexpr ContainerBase() : _items{} {};
virtual constexpr const T& operator()(std::size_t i) const = 0;
};
template <typename T, std::size_t N>
class Container : ContainerBase<T, N>
{
public:
constexpr Container(std::initializer_list<T> data) : ContainerBase<T, N>()
{
std::copy(data.begin(), data.end(), this->_items.begin());
}
constexpr const T& operator()(std::size_t i) const override
{
return this->_items[i];
}
};
Test on godbolt.
Another IntelliSense bug was spotted during this test: if _items
were not initialized in the base class constructor (_items{}
), IntelliSense will report this error, but the code can be compiled just fine:
Error E0028: expression must have a constant value