我正在尝试使用带有自定义结构 a 键的 unordered_map。我从其他帖子中知道,我需要定义自定义哈希函数和比较函数。我的问题如下:编译STATIC
和SHARED
库均无错误。但在执行时,test_exec
当我构建库时出现以下错误STATIC
:
AddressSanitizer:DEADLYSIGNAL
=================================================================
==19573==ERROR: AddressSanitizer: FPE on unknown address 0x561e54882917 (pc 0x561e54882917 bp 0x7ffea9e6f580 sp 0x7ffea9e6f580 T0)
#0 0x561e54882917 in std::__detail::_Mod_range_hashing::operator()(unsigned long, unsigned long) const (/home/TEST/minimal_example/build_DEBUG/test_exec+0x4917)
#1 0x561e5488855f in std::__detail::_Hash_code_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, test::MyEnum>, std::__detail::_Select1st, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>::_M_bucket_index(unsigned long, unsigned long) const (/home/TEST/minimal_example/build_DEBUG/test_exec+0xa55f)
AddressSanitizer:DEADLYSIGNAL
AddressSanitizer: nested bug in the same thread, aborting
我怎样才能让它为STATIC
图书馆服务?
测试库
#ifndef TEST_LIB_H
#define TEST_LIB_H
#include <string>
#include <unordered_map>
namespace test {
enum class MyEnum { A, B, C, D };
using enum MyEnum;
const std::unordered_map<std::string, MyEnum> string_to_enum = {
{"A", A},
{"B", B},
{"C", C},
{"D", D},
};
const std::unordered_map<MyEnum, std::string> enum_to_string = {
{A, "A"},
{B, "B"},
{C, "C"},
{D, "D"},
};
struct MyStruct {
MyEnum my_enum;
unsigned int num;
bool operator==(const MyStruct &other) const {
return (my_enum == other.my_enum) && (num == other.num);
}
};
struct MyStructHash {
std::size_t operator()(const MyStruct &s) const {
return std::hash<MyEnum>{}(s.my_enum) ^
(std::hash<unsigned int>{}(s.num) << 1);
}
};
using MyMap = std::unordered_map<MyStruct, double, MyStructHash>;
MyMap get_map();
const MyMap MAP = get_map();
} // namespace test
#endif
测试库.cpp
#include "test_lib.h"
namespace test {
MyMap get_map() {
MyMap map;
map[MyStruct(string_to_enum.at("A"), 2)] = 4.5;
return map;
}
} // namespace test
测试执行文件
#include "test_lib.h"
#include <iostream>
using namespace test;
int main(int ac, char **av) {
auto test = string_to_enum.at("A");
std::cout << enum_to_string.at(test) << std::endl;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
############################
# project name and version #
############################
project( Test VERSION 0.1 LANGUAGES CXX)
##############################
# set c++ flags and standard #
##############################
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -Wall -Wextra -Wpedantic -Wconversion -Wdouble-promotion -Wno-unused-parameter -Wno-unused-function -Wno-sign-conversion -fsanitize=address")
add_library(test_lib STATIC test_lib.cpp)
set_target_properties(test_lib PROPERTIES PUBLIC_HEADER test_lib.h)
add_executable(test_exec test_exec.cpp)
target_link_libraries(test_exec PUBLIC
test_lib
)
到目前为止,我已经构建了这个最小的示例来排除我的代码中的任何其他问题。
编辑:
定义后
extern const MyMap MAP;
在 test_lib.h 和
const MyMap MAP = get_map();
在 test_lib.cpp 中它可以工作。
事情是这样的。
const
全局变量也是隐式的static
(除非明确标记为extern
,但您的示例中并非如此)。因此,每个包含 的翻译单元test_lib.h
都会获得自己的string_to_enum
、enum_to_string
和实例MAP
。每个翻译单元内全局变量的初始化顺序是按顺序的,从上到下。但不同翻译单元之间的初始化顺序是不确定的。这可能会导致所谓的静态初始化顺序混乱- 这就是您所观察到的。
在您的示例中,与翻译单元关联的三个全局变量首先被初始化。 的初始化程序
test_exec
调用,它尝试使用。 但位于翻译单元中,因此 指的是与该单元关联的 的实例- 该实例尚未初始化。 因此,您的程序通过在对象的生命周期开始之前使用它来表现出未定义的行为。MAP
get_map()
string_to_enum
get_map()
test_lib
string_to_enum
解决方案是在头文件中声明全局变量
extern
,并在一个源文件中定义它们;因此程序中每个变量只有一个实例。(如果可能的话,更好的办法是完全避免使用全局变量。)