Следующий код выдает предупреждение в 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;
}
В любом случае это работает правильно, но это заставляет меня беспокоиться.
Каково ваше мнение, подвержены ли эти указатели «подчиненных» более жестким вопросам псевдонимов, чем простые указатели членов?
Я бы рекомендовал не делать это таким образом.
Вы указали в своих комментариях, что вы пытались использовать вложенный std::bind
, но есть проблема с версией компилятора, которую вы используете. Вместо того, чтобы прибегать к взлому, я бы бросил свой собственный повторный указатель на класс члена.
#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
с соответствующими аргументами.