Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour when using C++17 static inline members in Visual Studio

Yesterday I asked a question about this problem, but I wasn't able to give a MVCE. I've managed to reproduce this with a simple program. The problem is with using an std::list as a static inline declaration in a class. Microsoft Visual Studio does support this new C++17 feature. It had some bugs as of March, but as far as I know they've been fixed since. Here are instructions of how I can get this problem, this happens in debug mode.

In main.cpp

#include <iostream>
#include "header1.h"

int main()
{
    return 0;
}

In header1.h:

#include <list>

struct Boo
{
    static inline std::list<int> mylist;
};

In anotherCPP.cpp

#include "Header1.h"

When the program exits main() it destroys all the static objects and throws an exception.

If this doesn't crash, maybe on your system the compiler/linker optimised some code out, so you can try making main.cpp and anotherCPP.cpp do something. In anotherCPP.cpp:

#include <iostream>
#include "Header1.h"

void aFunction()
{
    std::cout << Boo::mylist.size();
}

And make main.cpp:

#include <iostream>
#include "Header1.h"

void aFunction();

int main()
{
    std::cout << Boo::mylist.size();
    afunction();

    return 0;
}

When the program exits I get an exception here when the std::list is being cleared. Here is the Visual Studio debug code where it crashes:

for (_Nodeptr _Pnext; _Pnode != this->_Myhead(); _Pnode = _Pnext)
{   // delete an element
    _Pnext = _Pnode->_Next; // Here: Exception thrown: 
                            // read access violation.
                            // _Pnode was 0xFFFFFFFFFFFFFFFF.

    this->_Freenode(_Pnode);
}

This happens only if I declare the static inline std::list< int > mylist in the class. If I declare it as static std::list< int > mylist in my class and then define it separately in one .cpp as std::list< int > Boo::mylist; it works fine. This problem arises when I declare the std::list static inline and I include the header for the class in two .cpp files.

In my project I have stepped through the std::list clear loop from above, I took note of the "this" pointer address. I stepped through the loop as it freed nodes in my list. It then came back to free other std::lists, including in std::unordered_map (as they also use std::lists from the looks of it). Finally when the read access exception is thrown and _Pnode is an invalid pointer address, I noticed the "this" pointer address is the same as the "this" pointer address when clearing std::list< int > mylist, which makes me think that it's trying to delete it twice, and probably why it's crashing.

I hope someone can reproduce this, I'm not sure what this is, if it's a bug or something I'm doing wrong. Also this happens for me in 32 and 64 bit, but only in debug mode, because the node freeing loop I provided is under a macro:

#if _ITERATOR_DEBUG_LEVEL == 2
like image 384
Zebrafish Avatar asked Oct 15 '18 04:10

Zebrafish


1 Answers

This issue was filed as a bug here under the title "Multiple initializations of inline static data member in Debug mode".

This was found in Visual Studio 2017 version 15.7.

The VS compiler team has accepted this and have fixed the problem in an upcoming release.

like image 126
P.W Avatar answered Oct 25 '22 15:10

P.W