内联类函数和共享库(dll)构建

Geo*_*ffW 6 c++ mingw inline shared-libraries dllexport

我正在尝试将一些代码移动到共享库中(在独立编译时工作正常)但是遇到了类内联函数的一些问题.mingw/gcc v4.7.2.

部分问题似乎是因为我更喜欢在类声明之外定义我的内联函数(它使类声明更整洁,更容易阅读).我一直认为这是可以接受的,等同于在类声明中定义......但似乎并非总是如此.我已经创建了一个简单的示例来演示问题.(显然dllexport通常会在宏中导入/导出.)

标题:

// Uncomment one at a time to see how it compiles with: -O2 -Winline
//#define INLINE_OPTION 1 // implicit - builds without inline warnings
#define INLINE_OPTION 2 // simple external inline - gives inline warnings
//#define INLINE_OPTION 3 // external forced inline - gives inline errors

class __attribute__((dllexport)) Dummy {
public:
    Dummy() : m_int{0} {}
    ~Dummy() {}
    #if INLINE_OPTION == 1
    int get_int() const { return m_int; }
    #else
    int get_int() const;
    #endif
    int do_something();
private:
    int m_int;
};

#if INLINE_OPTION == 2
inline int Dummy::get_int() const
{ return m_int; }
#endif

#if INLINE_OPTION == 3
inline __attribute__((always_inline)) int Dummy::get_int() const
{ return m_int; }
#endif
Run Code Online (Sandbox Code Playgroud)

.cpp文件:

int Dummy::do_something()
{
    int i = get_int();
    i *= 2;
    return i;
}
Run Code Online (Sandbox Code Playgroud)

如上所述,使用INLINE_OPTION == 1(隐式的,类内内联定义),代码将编译出警告.

使用INLINE_OPTION == 2(类外的内联定义),我收到此警告: int Dummy::get_int() const' can never be inlined because it uses attributes conflicting with inlining [-Winline]

使用INLINE_OPTION == 3(试图强制内联),我得到与上面相同的警告,我得到这个错误:error: inlining failed in call to always_inline 'int Dummy::get_int() const': function not inlinable,从.cpp文件中的Dummy :: do_something()内的第一行调用它的信息.请注意,这是关于尝试内联库本身的功能!对于简单的访问器功能,这可能是非常重要的开销.

难道我做错了什么?gcc是否正确地将类外定义内联函数与内部函数定义区别对待?(我真的被迫弄乱了班级宣言吗?)

注意:问题不仅影响我内联声明的内容.它还会影响声明为constexpr的任何内容,甚至在涉及继承时声明为"= default"的析构函数.

编辑:

刚尝试使用mingw64/gcc v4.8.0,结果相同.请注意,这包括选项1不在do_something中内联的事实(我检查了汇编程序输出),因此显然选项1和选项2之间的唯一区别是只有选项2将给出-Winline警告.

Geo*_*ffW 1

我在另一篇文章中所做的编辑似乎没有被采纳,无论如何,似乎一些额外的清晰度可能是合适的,所以我发布了我发送到另一个论坛的详细信息。下面的代码class C是解决此问题的方法 - 仅导出非内联成员,而不是整个类。正如其他地方的评论中所指出的,__declspec(dllexport)__attribute__((dllexport))是等效的。

测试.hpp

class __declspec(dllexport) A {
public:
    int fa() { return m; }
    int ga();
private:
    int m{0};
};

class __declspec(dllexport) B {
public:
    int fb();
    int gb();
private:
    int m{0};
};
inline int B::fb() { return m; }

class C {
public:
    int fc() { return m; }
    __declspec(dllexport) int gc();
private:
    int m{0};
};
Run Code Online (Sandbox Code Playgroud)

测试.cpp

#include "test.hpp"

int A::ga() { return (fa() + 1); }

int B::gb() { return (fb() + 1); }

int C::gc() { return (fc() + 1); }
Run Code Online (Sandbox Code Playgroud)

如果您使用以下选项进行编译:(-std=c++11 -O2 -S -Winline使用 mingw/ming64 和 gcc v4.7.2 或 v4.8.0),您可以看到为库函数 ga、gb 和 gc 生成的汇编程序如下所示:

嘎:

subq    $40, %rsp
.seh_stackalloc 40
.seh_endprologue
call    _ZN1A2faEv
addl    $1, %eax
addq    $40, %rsp
ret
Run Code Online (Sandbox Code Playgroud)

国标:

subq    $40, %rsp
.seh_stackalloc 40
.seh_endprologue
call    _ZN1B2fbEv
addl    $1, %eax
addq    $40, %rsp
ret
Run Code Online (Sandbox Code Playgroud)

GC:

.seh_endprologue
movl    (%rcx), %eax
addl    $1, %eax
ret
Run Code Online (Sandbox Code Playgroud)

你会收到警告:
warning: function 'int B::fb()' can never be inlined because it uses attributes conflicting with inlining [-Winline]
warning: inlining failed in call to 'int B::fb()': function not inlinable [-Winline] (called from B::gb())

请注意,没有关于 fa 未内联的警告(我认为这是预期的)。但还要注意 ga、gb 和 gc 都是库函数。无论您认为内联函数本身是否应该导出,没有充分的理由说明内联函数不能在库内内联。因此我认为这是编译器中的一个错误。

环顾一下备受推崇的代码,看看有多少代码只导出显式成员。例如,boost 的那些被编译到库中的部分(例如:正则表达式),使用该class A技术,这意味着许多访问器函数不会内联到库内。

但是,除此之外,现在的答案是class C技术(显然在实际代码中,这必须包含在宏中才能在导出和导入之间切换,就像通常在类级别一样)。