C++ 中的简单存储类和严格的别名

发布于 2024-10-02 08:49:29 字数 2033 浏览 6 评论 0原文

我有以下代码用于存储一个小类。

#include <iostream>

template<typename T>
class storage
{
private:
  struct destroy
  {
    T& m_t;
    destroy(T& t) : m_t(t) { }
    ~destroy() { m_t.~T(); }
  };

  char m_c[sizeof(T)];
  void* address() { return &m_c[0]; }

public:
  void set(const T& t) { new (address()) T(t); }

  T get()
  {
    T& t = *static_cast<T*>(address());
    destroy _d(t);
    return t;
  }

};

template<typename T>
class choosable_storage
{
private:
  union
  {
    T*         m_p;
    storage<T> m_storage;
  };
  bool m_direct;

public:
  choosable_storage() : m_direct(false) { }

  void set_direct(const T& t)
  {
    m_direct = true;
    m_storage.set(t);
  }

  void set_indirect(T* const t) { m_p = t; }

  T get()
  {
    if (m_direct) return m_storage.get();
    return *m_p;
  }

};

int main(void)
{
  storage<int> s; // no problems
  s.set(42);
  std::cout << s.get() << std::endl;

  int i = 10;

  choosable_storage<int> c1; // strict aliasing warnings
  c1.set_indirect(&i);
  std::cout << c1.get() << std::endl;

  choosable_storage<int> c2;
  c2.set_direct(i);
  std::cout << c2.get() << std::endl;

  return 0;
}

当我返回时,gcc 4.4 警告我违反了 storage::get() 中的严格别名规则。

AFAIK,我没有违反任何规则。我实际上违反了严格的别名还是 gcc 在这里变得挑剔?

有没有一种方法可以让它不发出警告而不禁用严格别名?

谢谢

编辑:

另一方面,以下实现不会发出任何警告:

template<typename T>
class storage
{
private:
  struct destroy
  {
    T& m_t;
    destroy(T& t) : m_t(t) { }
    ~destroy() { m_t.~T(); }
    T const& operator()() const { return m_t; }
  };

  char m_c[sizeof(T)];

public:
  void set(const T& t) { new(static_cast<void*>(m_c)) T(t); }

  T get(void) { return destroy(*static_cast<T*>(static_cast<void*>(m_c)))(); }

};

编辑:

gcc 4.5 及更高版本不会发出警告 - 所以显然这只是一个误解严格的别名规则或 gcc 4.4.x 中的错误

I have the following code for having a small class for storage.

#include <iostream>

template<typename T>
class storage
{
private:
  struct destroy
  {
    T& m_t;
    destroy(T& t) : m_t(t) { }
    ~destroy() { m_t.~T(); }
  };

  char m_c[sizeof(T)];
  void* address() { return &m_c[0]; }

public:
  void set(const T& t) { new (address()) T(t); }

  T get()
  {
    T& t = *static_cast<T*>(address());
    destroy _d(t);
    return t;
  }

};

template<typename T>
class choosable_storage
{
private:
  union
  {
    T*         m_p;
    storage<T> m_storage;
  };
  bool m_direct;

public:
  choosable_storage() : m_direct(false) { }

  void set_direct(const T& t)
  {
    m_direct = true;
    m_storage.set(t);
  }

  void set_indirect(T* const t) { m_p = t; }

  T get()
  {
    if (m_direct) return m_storage.get();
    return *m_p;
  }

};

int main(void)
{
  storage<int> s; // no problems
  s.set(42);
  std::cout << s.get() << std::endl;

  int i = 10;

  choosable_storage<int> c1; // strict aliasing warnings
  c1.set_indirect(&i);
  std::cout << c1.get() << std::endl;

  choosable_storage<int> c2;
  c2.set_direct(i);
  std::cout << c2.get() << std::endl;

  return 0;
}

gcc 4.4 warns that I break the strict aliasing rules in storage::get() when I return.

AFAIK, I do not violate any rules. Do I actually violate strict aliasing or is gcc getting picky here?

And is there a way of having it warning free without disabling strict aliasing?

Thanks

EDIT:

On the other hand, the following implementation does not give any warnings:

template<typename T>
class storage
{
private:
  struct destroy
  {
    T& m_t;
    destroy(T& t) : m_t(t) { }
    ~destroy() { m_t.~T(); }
    T const& operator()() const { return m_t; }
  };

  char m_c[sizeof(T)];

public:
  void set(const T& t) { new(static_cast<void*>(m_c)) T(t); }

  T get(void) { return destroy(*static_cast<T*>(static_cast<void*>(m_c)))(); }

};

EDIT:

gcc 4.5 and up does not issue a warning - so apparently this was just a misinterpretation of the strict aliasing rules or a bug in gcc 4.4.x

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

最初的梦 2024-10-09 08:49:29

我实际上违反了严格别名还是 gcc 在这里变得挑剔?

严格别名规则有两种不同的解释:

  • 通常的弱严格别名规则:(char/unsigned char 类型除外)您不能使用强制转换或union 来执行类型双关,您需要 memcpy (或两个 易失性 访问);这样的代码确实不太合理,并且在C(除了最新的、非常荒谬的C标准)和C++中被明确禁止。
  • 不寻常的强严格别名规则:不能重用内存:一旦内存区域具有“动态类型”(什么?),它就不能与其他类型一起使用。因此,您无法编写分配器函数(malloc 替代方案)C,除非它每次只调用 malloc/free

强规则定义不明确,破坏了许多合理的代码,并且出于某种原因由 GCC 维护者选择(完全基于自我欺骗和循环论证 - 这真的很丑陋)。为了使 C++ 代码能够在强大的严格别名优化下工作,G++ 的维护者为典型的 C++ 代码添加了悲观(基于更多的自我欺骗),以保持优化!

我不知道他们是否/何时意识到自己的错误,如果有疑问,只需禁用严格别名。无论如何,这只是一个非常小的优化。

Do I actually violate strict aliasing or is gcc getting picky here?

There are two different interpretation of the strict aliasing rule:

  • the usual weak strict aliasing rule: (except for char/unsigned char type) you cannot use a cast or union to perform type punning, you need memcpy (or two volatile accesses); such code really is not very reasonable, and is explicitly forbidden in C (except the latest, very absurd, C standard) and C++.
  • the unusual strong strict aliasing rule: you cannot reuse memory: once a region of memory has a "dynamic type" (a what?), it cannot be used with a different type. So you just cannot write an allocator function (malloc alternative) C, unless it only calls malloc/free each time.

The strong rule is ill-defined, break lots of reasonable code, and for some reason was chosen by the GCC maintainers (based entirely on self delusion and circular justifications - this is really ugly). To make C++ code work with the strong strict aliasing optimisation, the maintainers of G++ added pessimisations for typical C++ code (based on more self delusion), to keep the optimisation!

I do not know if/when they realise their mistake, in case of doubt just disable strict aliasing. It is a very minor optimisation anyway.

清音悠歌 2024-10-09 08:49:29

出于本问题的目的,严格的别名规则本质上规定,除非通过其自身类型的指针/引用或对字符类型(char 或 unsigned char)的指针/引用,否则不应访问对象。

在您的代码中,您有一个 char 类型元素的数组 m_c,并且您尝试通过 T 类型的引用访问它。这是严格的别名违规。在一些更奇特的平台上,这可能会产生影响,例如,如果 m_c 未正确对齐以保存类型 T 的元素。

For the purposes of this question, strict aliasing rule states, essentially, that you are not supposed to access an object except through a pointer/reference of its own type or a pointer/reference to a character type (char or unsigned char).

In your code, you have an array m_c of elements of type char, and you're trying to access it through a reference of type T. That's a strict aliasing violation. On some more exotic platforms, this could have repercussions, for example, if m_c is not properly aligned to hold the element of type T.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文