ODR bug in MSVC?
Asked Answered
D

1

20

This program prints 1 1 instead of 1 2 when compiled with MSVC (up to VS 2015).

f1.cpp:

#include <functional>

static std::function<int ()> helper() {
    struct F { int operator()() { return 1; } };
    return F();
}

std::function<int ()> f1() { return helper(); }

f2.cpp:

#include <functional>

static std::function<int ()> helper() {
    struct F { int operator()() { return 2; } };
    return F();
}

std::function<int ()> f2() { return helper(); }

main.cpp:

#include <functional>
#include <iostream>

std::function<int ()> f1();
std::function<int ()> f2();

int main() {
    std::cout << f1()() << " " << f2()() << "\n";
}

It's as if the different definitions of F are breaking ODR. But shouldn't local classes be distinct? Interestingly, if we replace F with lambda functions there is no conflict.

So is this a compiler bug or am I misunderstanding the one definition rule?

Doorsill answered 5/11, 2015 at 12:50 Comment(11)
Bit crazy that MSVC gets something like this wrong. It even gets it wrong if you remove the static and use an unnamed namespace.Spathose
What happens when you use two separate couts instead of one?Elery
Are you sure? With MSVC 14.0, if helper() is put in anonymous namespace then the conflict goes away.Doorsill
No point is separating couts.Doorsill
Huh. I'm getting 1 2 now. I swear I was getting 1 1 when I wrote that comment. :PSpathose
Nevermind it was my build environment. It wasn't detecting that f1.cpp and f2.cpp had changed.Spathose
I wonder if this is due to aggressive COMDAT folding you can turn it off by using /opt:noicfColet
Let me know if using /opt:noicf fixes it or not, it seems like a stretch but I have seen some interesting breaks due to this so it is worth a try.Colet
Getting the same behavior with /link /opt:noicf. I assume folding applies only to functions with identical definitions, which is not the case here.Doorsill
If you are using C++ >= 11, you could try moving F out of the function and into an anonymous namespace in your source files.Rainbow
I dont think it is a odr issue.Distilled
A
1

It's clearly a bug in MSVC, as all the types are unique. Perhaps for structs defined inside some function (in this case helper) MSVC internally treats them as if they are defined as helper::F while if helper is static it should have done something like this f1_cpp::helper::F instead. As a result at link time linker sees two identically named inline functions and merges them into one.

Most likely by reordering input files you can get 2 2 if you weren't happy with 1 1 :)

Archduchy answered 11/4, 2017 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.