c++ - 为什么具有静态变量的静态内联函数的行为如此不同?
问题描述
我很难理解为什么编译器和平台之间的行为会有如此大的差异。
这是http://kyungminlee.org/doc/minutiae/local_static_variable_shared_library.html的扩展示例。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.7.2)
project(static_inline)
set(CMAKE_CXX_STANDARD 14)
add_library(lib SHARED collect.h collect.cpp)
target_compile_definitions(lib PRIVATE BUILD_DLL)
add_executable(static_inline main.cpp)
target_link_libraries(static_inline PRIVATE lib)
enable_testing()
add_test(NAME test.static_inline
COMMAND static_inline)
收集.h
#pragma once
#ifdef _WIN32
# define STATIC
# ifdef BUILD_DLL
# define EXPORT __declspec(dllexport)
# else
# define EXPORT __declspec(dllimport)
# endif
#else
# define STATIC static
# define EXPORT
#endif
// static will not compile on windows since static inline has internal linkage
EXPORT STATIC inline int collect(int x)
{
static int sum = 0;
sum += x;
return sum;
}
EXPORT int get_sum();
struct EXPORT foo
{
static inline int collect(int x)
{
static int sum = 0;
sum += x;
return sum;
}
int get_sum();
};
收集.cpp
#include "collect.h"
int get_sum()
{
return collect(0);
}
int foo::get_sum()
{
return collect(0);
}
主文件
#include "collect.h"
#include <iostream>
int main()
{
int num_from_inline_function = collect(10);
int num_from_inline_function2 = get_sum();
std::cout << "static inline collect: " << num_from_inline_function << std::endl;
std::cout << "get_sum: " << num_from_inline_function2 << std::endl;
int num_from_inline_static_member_function = foo::collect(10);
int num_from_inline_static_member_function2 = foo().get_sum();
std::cout << "static inline foo::collect: " << num_from_inline_static_member_function << std::endl;
std::cout << "foo::get_sum: " << num_from_inline_static_member_function2 << std::endl;
return !(num_from_inline_static_member_function == num_from_inline_static_member_function2 &&
num_from_inline_function == num_from_inline_static_member_function2);
}
窗口输出:
MinGW-w64。
1: Test command: C:\dev\repos\static_inline\cmake-build-release-mingw-w64\static_inline.exe
1: Test timeout computed to be: 10000000
1: static inline collect: 10
1: get_sum: 0
1: static inline foo::collect: 10
1: foo::get_sum: 0
Failed
铛
1: Test command: C:\dev\repos\static_inline\cmake-build-release-mingw-w64\static_inline.exe
1: Test timeout computed to be: 10000000
1: static inline collect: 10
1: get_sum: 0
1: static inline foo::collect: 10
1: foo::get_sum: 0
Failed
MSVC
1: Test command: C:\dev\repos\static_inline\cmake-build-release-visual-studio\static_inline.exe
1: Test timeout computed to be: 10000000
1: static inline collect: 10
1: get_sum: 10
1: static inline foo::collect: 10
1: foo::get_sum: 10
100% tests passed, 0 tests failed out of 1
Ubuntu
GCC 和 Clang 给出相同的输出
1: Test command: /home/travis/build/ElDesalmado/static_inline_example/build/static_inline
1: Test timeout computed to be: 9.99988e+06
1: static inline collect: 10
1: get_sum: 0
1: static inline foo::collect: 10
1: foo::get_sum: 10
1/1 Test #1: test.static_inline ............... Passed 0.00 sec
100% tests passed, 0 tests failed out of 1
结果仅在 Ubuntu 上看起来有些一致。在 Windows 上,只有 MSVC 的行为有所不同。MSVC 不仅与 MinGW 和 Clang 不同,而且与 Ubuntu 上的 GCC 和 Clang 不同。
我想对于 Windows 上的 MSVC,我们看到的结果是因为链接器删除了内联函数和成员函数的重复符号:
- 标准是否要求链接器删除内联函数的重复符号?
对于内联函数,本地静态成员(我记得)保证在同一个地址:
同一内联函数(可能是隐式内联)的所有定义中的函数局部静态对象都指在一个翻译单元中定义的同一对象。
2. 带有函数局部静态对象的静态成员内联函数怎么样?它们是否在翻译单元中引用相同的对象?
- Windows 上的 MSVC 显然说YES。这种行为在编译中是否一致?
- Windows 上的 MinGW 和 Clang说不
- Ubuntu 上的 GCC 和 Clang 说YES。
- 标准对此有何评论?
- 对于加载插件的库或核心应用程序,依赖静态内联函数和静态内联成员函数对 ABI 有多友好?例如,如果类型 id 存储为内联静态函数/内联静态成员函数中的函数局部静态对象?
- 例如,我知道对内联函数体的任何更改都会破坏 ABI。但除此之外呢?
class __declspec(dllexport) counter
{
static int get() // implicitly inline
{
static int current = 0;
return current++;
}
};
解决方案
使用#define STATIC static
, ::collect
thatmain
调用只是与调用的函数不同::get_sum
的函数,所以我不知道您期望的其他行为。这不适用于静态或其他成员函数;static
在这里意味着完全不同的东西,并且所有定义的多个定义foo
都定义了具有相同成员函数的相同类型。
推荐阅读
- php - 从 foreach 中的数组中删除一个数组
- sql-server - 对多个连接的错误进行故障排除,其中一个表具有 group-by 语句中的变量的最小值
- r - 如何将 ISO8601 日期表示为日月?
- python - 给一个键中的一个元素,并在字典的不同键上获取对应的元素
- c# - 变量“crystalReportViewer1”要么未声明,要么从未在 Designer.cs 中分配
- javascript - 如何更改对象内部的键:JavaScript
- python - 如何让 Python 程序加载和使用几个不同的同名包?
- java - 回文程序没有按预期工作
- gradle - 如何查看可以为 gragle.properties 设置的所有选项
- python - 我创建了一个不和谐级别的机器人,但它得到错误说未定义