C++严格的别名规则和成员指针

4
以下代码在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;
}

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

你的意见是什么,这些“子成员”指针比普通成员指针更容易受到严格别名问题的影响吗?


1
这就是让你担心的事情吗?你正在做很多关于编译器如何实现指向成员的指针的假设,并使用它来创建指向子成员的自己的指针。整件事似乎有点冒险和不可移植。你想要达到什么目的? - Dave S
我完全理解该方法本身可能是不可移植的,无法与虚基类进行交互(尽管将其转换为size_t应该会失败),并且是C++编程中不建议使用的所有内容 =) - Fedor Trushkin
我尝试这个的原因相当理论化。事实上,指向普通(实际上是POD)类成员的指针编译时常量偏移量。成员指针可以指向给定类型的任何成员。成员类的任何成员的偏移量也从类开头开始固定。然后想象一下,我们需要一个修改深层成员的功能对象?或者,一个成员项是C数组。语言规则禁止您直接引用该成员,因此需要多个绑定。 - Fedor Trushkin
无论如何,我仍然在想,除了执行reinterpret_cast和依赖于G++内部成员指针表示之外,这种肮脏的黑客是否还有任何违反C++标准的矛盾。好吧,你可能会说:没有必要推理任何从禁止使用reinterpret_cast开始的事情。不过,看起来计算出的值与普通成员指针有很多共同点,与它相比应该没有额外的副作用。 - Fedor Trushkin
我不建议任何人练习这种令人厌恶的黑客行为,而且我认为最终我会坚持使用绑定的解决方案(我猜它应该优化到相同的常数积分偏移量)。 虽然拥有一个轻量级的成员指针看起来很诱人,可以访问成员的成员等等。=) - Fedor Trushkin
有趣的是:原来bind(&A::x, bind(&B::a, bind(&C::b, _1)))在gcc 4.8.3之前的编译器中会出现错误。因此,在下一个Qt版本发布之前,我必须使用上述技巧来解决这个问题。-) - Fedor Trushkin
1个回答

0

我建议不要以这种方式进行操作。

你在评论中提到尝试使用嵌套的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_ptr或shared_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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接