代码之家  ›  专栏  ›  技术社区  ›  andreas buykx

具有不同内联函数定义的函数对象的实例化取决于链接的顺序。

  •  1
  • andreas buykx  · 技术社区  · 15 年前

    请帮助我了解以下行为的根本原因。

    在文件中 a.cpp 我有:

    namespace NS {
       struct Obj {
          void pong(){ cout << "X in "__FILE__ << endl; }
          double k;
       };
       X::X() { Obj obj; obj.pong(); }
       void X::operator()() { cout << "X says hello" << endl; }
    }
    

    在文件中 b.cpp 我有:

    namespace NS {
       struct Obj {
          void pong(){ cout << "Y in "__FILE__ << endl; }
          bool m;
       };
       Y::Y() { Obj obj; obj.pong(); }
       void Y::operator()() { cout << "Y says hello" << endl; }
    }
    

    我的 main 创建X、Y并调用其运算符()s:

    int main( int argc, char *argv[] )
    {
       NS::X x;
       x();
    
       NS::Y y;
       y();
    
       return 0;
    }
    

    这个程序的输出取决于 A.CPP 捕获信号 首先编译:在第一种情况下 Obj A.CPP 也在 NS::Y 的构造函数,在第二种情况下, 对象 捕获信号 在两者中都实例化 NS::X N::Y .

    % g++ b.cpp a.cpp main.cpp
    % ./a.out
    
    X in a.cpp
    X says hello
    Y in b.cpp
    Y says hello
    
    % g++ b.cpp a.cpp main.cpp
    % ./a.out
    
    Y in b.cpp
    X says hello
    Y in b.cpp
    Y says hello
    

    Linux或Visual Studio(2005)上没有来自链接器的警告。如果我定义 Obj::pong() 在结构的声明之外,我得到一个链接器错误,告诉我obj::pong函数是乘法定义的。

    我做了进一步的实验,发现原因一定与内联是否相关,因为如果我用-o3编译,每个对象都使用他自己翻译单元的obj。

    那么问题变为:在非优化编译期间内联函数的第二个定义会发生什么?他们是默默地被忽视了吗?

    2 回复  |  直到 15 年前
        1
  •  2
  •   Johannes Schaub - litb    15 年前

    这是未定义的行为:类定义定义了相同的类类型,因此它们必须是相同的。对于链接器,这意味着它可以选择一个任意定义作为发出的定义。

    如果希望它们是分离的类型,则必须将它们嵌套到未命名的命名空间中。这将导致该命名空间中的任何内容对于该翻译单元都是唯一的:

    namespace NS {
       namespace {
       struct Obj {
          void pong(){ cout << "Y in "__FILE__ << endl; }
          bool m;
       };
       }
       Y::Y() { Obj obj; obj.pong(); }
       void Y::operator()() { cout << "Y says hello" << endl; }
    }
    

    那么问题变为:在非优化编译期间内联函数的第二个定义会发生什么?他们是默默地被忽视了吗?

    是的,对于内联函数(类定义中定义的函数是内联的,即使没有显式地声明为内联的),同样的原则也适用:它们可以在程序中定义多次,并且程序的行为就像它只定义一次一样。对链接器来说,这意味着它可以放弃除一个定义之外的所有定义。它选择哪一个是未指明的。

        2
  •  0
  •   avp    15 年前

    链接器处理损坏的名称。 请看这里: http://en.wikipedia.org/wiki/Name_mangling

    因此,正如约翰内斯所说,这种行为是不明确的,但细节可能会被澄清:
    如果pong()是在命名空间之外定义的,那么它的名称将变为唯一的,并且链接器会正确地发出抱怨。

    但是,如果名称隐藏在名称空间中,并且与另一个翻译单元中的同一个名称重叠(正如您所发现的那样),则链接器不会抱怨。它只使用一个符号。
    就是这样。

    我认为,它没有被指定,并且对于任何编译器/链接器都是特定于实现的。