I came across an issue that leads to a segfault and I'm totally in the dark as to why this is happening. I managed to recreate the issue in a fairly minimal working example, see below.
I was trying to construct multiple std::unordered_maps in different CUs where each value is a std::function, initialized by a lambda. Each CU contains a different implementation of the map where the return value of the std::function may vary, because different classes need different implementations of this map to do their thing.
Now the issue arises when the custom return type has the same name but a different implementation. In each CU, I defined a struct HandlerReturn. I didn't choose unique names because they won't see each other anyway. When accessing the lambda, the program segfaults. The program works when I simply rename HandlerReturn to HandlerReturn2 in the second CU. Here is the minimal working example. The example was tested on Linux using G++ 13.3.0, LDD 2.39.
I want to know why this happens. I can already fix the issue by using (unnamed) namespaces or renaming things, but I was always under the impression that separate translation units won't have name clashes anyway.
// main.cc
#include <iostream>
void fun1(std::string const &key);
void fun2(std::string const &key);
int main() {
fun1("string1");
fun2("string1");
}
// file1.cc
#include <functional>
#include <unordered_map>
#include <iostream>
struct HandlerReturn {
bool state = false;
int value = 0;
};
using Handler = std::function<HandlerReturn()>;
std::unordered_map<std::string, Handler> const _map1 = {
{
"string1", []() -> HandlerReturn { return {true, 1}; }
},
{
"string2", []() -> HandlerReturn { return {true, 2}; }
}
};
void fun1(std::string const &key) {
if (not _map1.contains(key)) {
std::cout << "No match\n";
return;
}
HandlerReturn match = _map1.at(key)();
std::cout << "State: " << match.state << ", Value: " << match.value << '\n';
}
#include <functional>
#include <unordered_map>
#include <iostream>
struct HandlerReturn {
bool state = false;
std::string str1;
std::string str2;
};
using Handler = std::function<HandlerReturn()>;
std::unordered_map<std::string, Handler> const _map2 = {
{
"string1", []() -> HandlerReturn { return {true, "Hello", "World"}; }
},
{
"string2", []() -> HandlerReturn { return {true, "Foo", "Bar"}; }
}
};
void fun2(std::string const &key) {
if (not _map2.contains(key)) {
std::cout << "No match\n";
return;
}
auto match = _map2.at(key)();
std::cout << "State: " << match.state << ", Str1: " << match.str1 << ", Str2: " << match.str2 << '\n';
}
You are violating the ODR, so your program has Undefined Behavior. That's the answer to "why this happens".
Quoting cppreference:
Only one definition of any variable, function, class type, enumeration type, concept(since C++20) or template is allowed in any one translation unit (some of these may have multiple declarations, but only one definition is allowed).
and there is even an example, the same as in your question:
if one .cpp file defines
struct S { int x; };and the other .cpp file definesstruct S { int y; };, the behavior of the program that links them together is undefined.
For the solution, as already mentioned in comments, you should use unnamed namespaces.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With