首页 > 解决方案 > 为什么具有静态变量的静态内联函数的行为如此不同?

问题描述

我很难理解为什么编译器和平台之间的行为会有如此大的差异。

这是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,我们看到的结果是因为链接器删除了内联函数和成员函数的重复符号:

  1. 标准是否要求链接器删除内联函数的重复符号?
    对于内联函数,本地静态成员(我记得)保证在同一个地址:

同一内联函数(可能是隐式内联)的所有定义中的函数局部静态对象都指在一个翻译单元中定义的同一对象。
2. 带有函数局部静态对象的静态成员内联函数怎么样?它们是否在翻译单元中引用相同的对象?

  1. 对于加载插件的库或核心应用程序,依赖静态内联函数和静态内联成员函数对 ABI 有多友好?例如,如果类型 id 存储为内联静态函数/内联静态成员函数中的函数局部静态对象?
    • 例如,我知道对内联函数体的任何更改都会破坏 ABI。但除此之外呢?
class __declspec(dllexport) counter
{
   static int get() // implicitly inline
   {
      static int current = 0;
      return current++;
   }
};

标签: c++linkerlanguage-lawyercross-platform

解决方案


使用#define STATIC static, ::collectthatmain调用只是与调用的函数不同::get_sum的函数,所以我不知道您期望的其他行为。这不适用于静态或其他成员函数;static在这里意味着完全不同的东西,并且所有定义的多个定义foo都定义了具有相同成员函数的相同类型。


推荐阅读