在不违反 ODR 的情况下在程序启动时自动执行代码

Executing code automatically at program start without violating ODR

我尝试编写一种简单的方法来在程序启动时自动执行代码(不使用 not portable attribute())。

我写了下面的代码,我想问的是,如果在 main() 之前编写的代码放在头文件中,是否违反了 ODR。 或者,内联函数和变量是否阻止了这种情况? 如果违反了 ODR,是否会出现函数被调用两次的情况?

#include <iostream>

//-----

template <void(*init_function)()>
class execute_at_start
{
    struct dummy final {};
    ~execute_at_start() = delete;
    inline static dummy execute_() { init_function(); return dummy{}; }
    inline static dummy auto_init_ = execute_();
};

//-----

inline void echo(){ std::cout << "echo" << std::endl; }

template class execute_at_start<&echo>;

int main()
{
    std::cout << "EXIT SUCCESS" << std::endl;
    return EXIT_SUCCESS;
}

提前致谢

您的解决方案应该是 ODR 安全的。 C++ 标准要求对于 execute_at_start<> class 模板的每个不同实例只有一个静态数据成员实例 auto_init_ 并且它被初始化一次。

Itanium C++ ABI(大多数编译器都遵守它,MSVC 除外)需要全局变量的保护变量(auto_init_ 此处)以确保它们被初始化一次:

If a function-scope static variable or a static data member with vague linkage (i.e., a static data member of a class template) is dynamically initialized, then there is an associated guard variable which is used to guarantee that construction occurs only once.

有关详细信息,请参阅 Itanium C++ ABI §2.8 Initialization Guard Variables


针对此问题的另一个不依赖保护变量的标准 ODR 安全解决方案是 Schwarz Counter。自 C++ 诞生以来,它就被用于初始化标准流(std::cout 和朋友们)。在此解决方案中,counter 是一个具有外部链接的显式保护变量,它确保只有第一次调用 execute_at_start 构造函数调用 init_function:

// Header begin.
#include <iostream>

template<void(*init_function)()>
class execute_at_start {
    static inline int counter = 0;
public:
    execute_at_start() {
        if(!counter++)
            init_function();
    }
};

inline void echo() { std::cout << "echo" << std::endl; }

execute_at_start<echo> const execute_at_start_echo; // A copy in each translation unit.

// Header end.

int main() {}