0

While working on my own array types, I encountered this issue, where one of my unit tests passes for Clang, but fails on MSVC with the following messages:

error C7595: 'UnitTest': call to immediate function is not a constant expression  
note: a non-constant (sub-)expression was encountered  
note: the call stack of the evaluation (the oldest call first) is  
note: while evaluating function 'void UnitTest(void)'  

https://godbolt.org/z/KrbWMe8sM

Code is:

#include <exception>
#include <memory>

template <typename ElementType, size_t ElementCount>
class StaticArray
{

public:

    template <typename OA>
    constexpr StaticArray(OA&& otherArray)
        : m_cxBuffer(std::allocator<ElementType>().allocate(static_cast<size_t>(ElementCount)))
    {
        using OtherArrayType = decltype(otherArray);
        using OtherArrayTypeNoRef = std::remove_reference_t<OtherArrayType>;

        m_count = std::extent_v<OtherArrayTypeNoRef>;

        ElementType* src = otherArray;
        ElementType* srcEnd = src + m_count;
        ElementType* dst = m_cxBuffer;

        ElementType* srcCurrent = src;
        ElementType* dstCurrent = dst;
        while (srcCurrent != srcEnd)
        {
            std::construct_at(dstCurrent, std::move(*srcCurrent));
            ++srcCurrent;
            ++dstCurrent;
        }
    }

    constexpr ~StaticArray() noexcept
    {
        while (m_count > 0)
            std::destroy_at(m_cxBuffer + (--m_count));

        std::allocator<ElementType>().deallocate(m_cxBuffer, static_cast<size_t>(ElementCount));
    }

    size_t m_count = 0;
    ElementType* m_cxBuffer = nullptr;

}; // class StaticArray


////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////


consteval void UnitTest()
{
    size_t cppArray[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
    StaticArray<size_t, 15> sArr = std::move(cppArray);  // line 54
}
static_assert((UnitTest(), true), "failed!");

Removing the std::move from line 54 makes it work correctly in this whittled-down example, but is not acceptable for the full version where I want to distinguish between moving and copying.

Is this a bug with MSVC, or is Clang incorrect in accepting this code?

11
  • 11
    Thanks a lot for providing a link to Godbolt, but please embed everything needed to answer the question into the question's body itself. Links tend to expire.
    – yeputons
    Commented May 18 at 23:55
  • 1
    Note that it's enough to remove all template parameters and fields from StaticArray, as well as the whole implementation of the constructor.
    – yeputons
    Commented May 19 at 0:02
  • 1
  • 2
    Let's see if I can fit the minimized example ("remove all template parameters and fields from StaticArray, as well as the whole implementation of the constructor") in a comment. If it fits in a comment, it fits in a question, no? #include <memory> struct StaticArray { template <typename OA> constexpr StaticArray(OA&& otherArray) {} constexpr ~StaticArray() = default; }; // class StaticArray consteval void UnitTest() { size_t cppArray[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 }; StaticArray sArr = std::move(cppArray); } static_assert((UnitTest(), true), "failed!");
    – JaMiT
    Commented May 19 at 2:10
  • 1
    @PepijnKramer The very page you link to says [[nodiscard]] constexpr T* allocate( std::size_t n ); (since C++20) Commented May 19 at 6:59

0

Browse other questions tagged or ask your own question.