c++visual-c++g++constexprinitializer-list

constructor body not called in constexpr context in msvc


I am trying to pass an initializer list to the constructor of a struct, whose parameter is std::vector, but I am getting unexpected results in Visual Studio Community. Here is the code to reproduce the problem:

#include <vector>
#include <iostream>

using namespace std;

struct ST
{
    std::size_t size1;
    std::size_t size2;

    constexpr ST(std::vector<int> production) : size1(production.size())
    {
        size2 = production.size();
    }
};

static constexpr auto get_ST()
{
    return ST({ 1, 2, 3 });
}

int main()
{
    auto par = get_ST();
    std::cout << par.size1 << " " << par.size2 << std::endl;

    constexpr auto par2 = get_ST();
    std::cout << par2.size1 << " " << par2.size2 << std::endl;
}

On Visual Studio Community, the output is:

3 3
3 0

while in gcc, I am getting the following output (https://godbolt.org/z/reqqdcEEh):

3 3
3 3

From the output, it is clear that MSVC is not calling the body of constructor when the result is evaluated in constexpr context. Does anyone know if this is Standard C++ compliant or the bug in MSVC?


Solution

  • Yes, this is a bug in MSVC - interestingly, it is a regression; this worked under MSVC 19.28 (VS 16.9), the first version to support constexpr destructor, but not in later versions.

    A reduced version is:

    struct V {
        int z = 1;
        constexpr ~V() { z = 2; }
    };
    struct S {
        int i, j;
        constexpr S(V v) : i(v.z) {
            j = v.z;
        }
    };
    int main() {
        constexpr S s{V{}};
        return s.i * 10 + s.j;
    }
    

    A workaround is to take the constructor argument by const reference or by rvalue reference (or both); hopefully this will work for you.

    I've reported this to Microsoft at https://developercommunity.visualstudio.com/t/Premature-constexpr-destruction-of-by-va/10547472 so you can track the issue there.