gpt4 book ai didi

c++ - 在头文件中声明全局常量对象

转载 作者:可可西里 更新时间:2023-11-01 16:48:34 28 4
gpt4 key购买 nike

在 Eric Niebler 的 range-v3 库中,他提供了许多 header ,每个 header 都有自己的全局函数对象。它们都以相同的方式声明。他提供了一个类模板static_const :

template<typename T>
struct static_const
{
static constexpr T value {};
};

template<typename T>
constexpr T static_const<T>::value;

然后每个 F 类型的函数对象声明为:

namespace
{
constexpr auto&& f = static_const<F>::value;
}

通过 static_const 模板在未命名的命名空间中引入对象与仅仅编写相比有什么好处:

static constexpr F f{};

最佳答案

问题基本上是一个定义规则。

如果你有:

static constexpr F f{};

名字f有内部链接,这意味着每个翻译单元都有自己的 f .其结果意味着,例如,一个内联函数,其地址为 f。将根据调用发生在哪个翻译单元获得不同的地址:

inline auto address() { return &f; } // which f??

这意味着现在我们实际上可能有 address 的多个定义.真的,任何采用地址 f 的操作是可疑的。

来自 D4381 :

// <iterator>
namespace std {
// ... define __detail::__begin_fn as before...
constexpr __detail::_begin_fn {};
}

// header.h
#include <iterator>
template <class RangeLike>
void foo( RangeLike & rng ) {
auto * pbegin = &std::begin; // ODR violation here
auto it = (*pbegin)(rng);
}

// file1.cpp
#include "header.h"
void fun() {
int rgi[] = {1,2,3,4};
foo(rgi); // INSTANTIATION 1
}

// file2.cpp
#include "header.h"
int main() {
int rgi[] = {1,2,3,4};
foo(rgi); // INSTANTIATION 2
}

The code above demonstrates the potential for ODR violations if the global std::begin function object is defined naïvely. Both functions fun in file1.cpp and main in file2.cpp cause the implicit instantiation foo<int[4]>. Since global const objects have internal linkage, both translation units file1.cpp and file2.cpp see separate std::begin objects, and the two foo instantiations will see different addresses for the std::begin object. That is an ODR violation.


另一方面,通过:

namespace
{
constexpr auto&& f = static_const<F>::value;
}

同时 f仍然有内部链接,static_const<F>::value由于它是静态数据成员,因此具有外部 链接。当我们取 f 的地址时, 它是一个引用意味着我们实际上正在使用 static_const<F>::value 的地址,它在整个程序中只有一个唯一地址。

另一种方法是使用变量模板,它需要具有外部链接 - 这需要 C++14,并且也在同一链接中进行了演示:

namespace std {
template <class T>
constexpr T __static_const{};
namespace {
constexpr auto const& begin =
__static_const<__detail::__begin_fn>;
}
}

Because of the external linkage of variable templates, every translation unit will see the same address for __static_const<__detail::__begin_fn>. Since std::begin is a reference to the variable template, it too will have the same address in all translation units.

The anonymous namespace is needed to keep the std::begin reference itself from being multiply defined. So the reference has internal linkage, but the references all refer to the same object. Since every mention of std::begin in all translation units refer to the same entity, there is no ODR violation ([basic.def.odr]/6).


在 C++17 中,有了新的内联变量特性,我们根本不必担心这个,只需编写:

inline constexpr F f{};

关于c++ - 在头文件中声明全局常量对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35958508/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com