根据奇怪的因素调用不同版本的析构函数

Different versions of destructor called depending on strange factors

为什么下面的代码会产生这样的输出?

main.cpp ctor 0x24a4c30
test.cpp dtor 0x24a4c30

test.cpp

#include <boost/optional.hpp>

struct Test
{
    Test()
    {
        printf("test.cpp ctor %p\n", (void *) this);
    }

    ~Test()
    {
        printf("test.cpp dtor %p\n", (void *) this);
    }
};

boost::optional< Test > t;

main.cpp

#include <memory>

struct Test
{
    Test()
    {
        printf("main.cpp ctor %p\n", (void *) this);
    }

    ~Test()
    {
        printf("main.cpp dtor %p\n", (void *) this);
    }
};

int
main(void)
{
    std::make_shared< Test >();
    return 0;
}

编译为

g++ -std=c++11 -c test.cpp -o test.o
g++ -std=c++11 -c main.cpp -o main.o
g++ -std=c++11 test.o main.o

我解释了这种行为,即 test.o 提供测试的 ctor & dtor 然后链接器丢弃来自 main.o,但它只对 dtor 正确。如果我删除静态对象 t 然后链接器丢弃来自 test.o 的符号并且输出是 next

main.cpp ctor 0x208ec30
main.cpp dtor 0x208ec30

如评论中所述,在两个链接的翻译单元中定义相同的符号违反了 One Definition Rule,而

There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type, enumeration type, inline function with external linkage, inline variable with external linkage (since C++17), class template, non-static function template, static data member of a class template, member function of a class template, partial template specialization, concept (since C++20) as long as all of the following is true:

  • each definition consists of the same sequence of tokens (typically, appears in the same header file)
  • [...]

If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined. (emphasis mine)

UB的观察结果不需要解释,也可能没有。

当然,这是公然违规。然而,在看起来更无辜的程序中可能会出现非常相似的情况(并导致奇怪且难以定位的错误)。例如,当某些 class 声明更改时,链接到一个较旧的单独编译的对象。