【发布时间】:2021-06-24 19:44:12
【问题描述】:
我很难理解为什么编译器和平台之间的行为会有如此大的差异。
这是http://kyungminlee.org/doc/minutiae/local_static_variable_shared_library.html 的扩展example。
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);
}
main.cpp
#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);
}
Windows 输出:
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 说 NO
- Ubuntu 上的 GCC 和 Clang 说 YES。
- 标准对此有何规定?
-
为加载插件的库或核心应用程序依赖静态内联函数和静态内联成员函数对 ABI 的友好程度如何?例如,如果类型 id 存储为函数本地静态内联静态函数/内联静态成员函数中的对象?
- 例如,我知道对内联函数体的任何更改都会破坏 ABI。但除此之外呢?
class __declspec(dllexport) counter
{
static int get() // implicitly inline
{
static int current = 0;
return current++;
}
};
【问题讨论】:
-
C++ 标准不知道动态库的存在;因此,一旦您使用一个,您就会进入实现定义的行为。
-
(4) 无法回答(就这个问题而言),因为它与 [language-lawyer] 标签冲突;也就是说,动态库不是标准的一部分。
-
@RichardCritten C 标准有
An inline definition of a function with external linkage shall not contain a definition of a modifiable object with static or thread storage duration, and shall not contain a reference to an identifier with internal linkage.C++ 标准对此有什么说法吗?这并没有说明动态库。 -
在这里阅读 解释 en.cppreference.com/w/cpp/language/inline 它似乎没有相同的限制。当我们得到内联变量时(不确定这是否有影响)只是试图限制范围
-
@RichardCritten 从那个解释看来,在 Windows 上只有 MSVC 符合标准,即
Function-local static objects in all function definitions are shared across all translation units (they all refer to the same object defined in one translation unit)。然而在 Ubuntu 上却相反,看起来虽然一致但依赖于实现。
标签: c++ linker language-lawyer cross-platform