【问题标题】:C++ strict aliasing rules and pointers to membersC++ 严格的别名规则和指向成员的指针
【发布时间】:2014-07-26 08:16:19
【问题描述】:

以下代码在 G++ 中产生警告:

#include <iostream>
#include <cstdint>

template <typename T, typename P, typename Q>
Q T::*pointer_to(P T::*p, Q P::*q)
{
   typedef Q T::* output_ptr;
// warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
   size_t tmp = reinterpret_cast<const size_t&>(p) + reinterpret_cast<const size_t&>(q);
   return reinterpret_cast<const output_ptr&>(tmp);
}

struct A { int x; };
struct B { A a; };

int main()
{
   B b = B();
   b.*pointer_to(&B::a, &A::x) = 1;
   std::cout << b.a.x << std::endl;
}

无论如何它都能正常工作,但这让我担心。

您的看法是,这些“子成员”指针是否比普通成员指针更容易受到更严格的别名问题的影响?

【问题讨论】:

  • 那是什么让你担心?您对编译器如何实现指向成员的指针进行了很多假设,并使用它来创建自己的指向子成员的指向成员的指针。整个事情似乎有点冒险和不可移植。你想完成什么?
  • 当然我知道该方法本身可能是不可移植的,不能与虚拟基础正确互操作(尽管,转换为 size_t 应该会失败),并且是不建议使用的所有内容在 C++ 编程中 =)
  • 我尝试它的原因是理论上的。指向普通(实际上是 POD)类成员的指针实际上 一个编译时常量偏移量。指向成员的指针可以指向给定类型的任何成员。成员类的任何成员的偏移量也具有从类开始的固定偏移量。那么想象一下,我们需要一个功能对象来修改那个深入挖掘的成员?或者,作为 C 数组的成员的一项。语言规则禁止您直接引用该成员,因此需要多个绑定。
  • 无论如何,我仍然想知道,除了执行 reinterpret_cast-s 和依赖指向成员表示的 G++ 内部指针之外,这种肮脏的 hack 是否与 C++ 标准有任何额外的矛盾。好吧,你可能会说:从执行禁止的 reinterpret_cast 开始的任何事情都没有用推理。尽管如此,看起来计算值与普通成员指针有很多共同点,与它相比应该没有额外的副作用。
  • 我不建议任何人练习这种 diguisting hacking,我认为我最终会坚持使用绑定的解决方案(我想它应该优化到相同的恒定积分偏移量)。尽管拥有一个指向成员成员的轻量级指针等看起来很诱人。=)

标签: c++ strict-aliasing member-pointers


【解决方案1】:

我建议不要这样做。

您在 cmets 中声明您尝试使用嵌套的 std::bind,但您使用的编译器版本存在问题。与其诉诸 hack,不如将我自己的重复指针滚动到成员类。

#include <iostream>
#include <cstdint>
#include <type_traits>
#include <utility>


template<typename Ptr1, typename... Rest>
class pointer_to_sub;

template<typename ObjType, typename Class>
class pointer_to_sub<ObjType Class::* >
{
  typedef ObjType Class::* ptr_type;

public:
  typedef ObjType value_type;
  typedef Class input_type;
  pointer_to_sub(ptr_type input)  : ptr(input)
  {

  }

  value_type& operator()(input_type& from) const
  {
    return from.*ptr;
  }

  value_type const& operator()(input_type const& from) const
  {
    return from.*ptr;
  }

  value_type& operator()(input_type* from) const
  {
    return from->*ptr;
  }

  value_type const& operator()(input_type const* from) const
  {
    return from->*ptr;
  }



  private:

  ptr_type ptr;
};


template<typename ObjType, typename Class, typename... Rest >
class pointer_to_sub<ObjType Class::*, Rest...> : private pointer_to_sub<Rest...>
{
  typedef ObjType Class::* ptr_type;
  typedef pointer_to_sub<Rest...> base_type;
public:
  typedef typename base_type::value_type value_type;
  typedef Class input_type;

  pointer_to_sub(ptr_type input, Rest... args) : base_type(args...), ptr(input)
  {

  } 

  value_type& operator()(input_type& from) const
  {
    return base_type::operator()(from.*ptr);
  }

  value_type const& operator()(input_type const& from) const
  {
    return base_type::operator()(from.*ptr);
  }


  value_type& operator()(input_type* from) const
  {
    return base_type::operator()(from->*ptr);
  }

  value_type const& operator()(input_type const* from) const
  {
    return base_type::operator()(from->*ptr);
  } 
private:
  ptr_type ptr;
};

template<typename T, typename... Args>
pointer_to_sub<T, Args...> make_pointer_to_sub(T t1, Args... args)
{
  return pointer_to_sub<T, Args...>(t1, args...);
}

上面基本上提供了一个make_pointer_to_sub,它接受一个成员对象指针列表。它接受一个引用或可转换为第一种类型的指针作为其输入,然后依次取消引用每个指针。可以改进以接受unique_ptrshared_ptr,但那是以后的事了。如下所示使用它。

struct A { int x; double y;};
struct B { A a; };

int main()
{
  auto ptr = make_pointer_to_sub(&B::a, &A::x);


   B b = B();
   ptr(b) = 1;
   // b.*pointer_to(&B::a, &A::x) = 1;

   std::cout << b.a.x << std::endl;
   ptr(&b) = 2;
   std::cout << b.a.x << std::endl;

}

如果需要,可以使用适当的参数将其分配给 std::function

【讨论】:

    猜你喜欢
    • 2014-07-13
    • 1970-01-01
    • 2018-03-24
    • 2013-05-02
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    相关资源
    最近更新 更多