Как автоматически сгенерировать operator = в C ++? - PullRequest
1 голос
/ 17 октября 2019

У меня есть следующая структура шаблона:

 template<class T> struct S {
    T a,b,c,d,e,f,g,h;
 };

Затем я пытаюсь сделать это:

S<double> sDouble;
S<float> sFloat;

sDouble = sFloat; //Compile error here, since sDouble and sFloat have «unrelated» types

Что мне делать? У меня есть много таких структур с большим количеством элементов данных. Я устал от предоставления пользовательских шаблонов operator= для каждой структуры, чтобы потом выяснить, что я забыл назначить некоторый элемент.

PS Ситуация выше очень противоречит приведенному ниже коду, который можно скомпилировать:

template<class T> using V = T; 

int main()
{
  V<double> vDouble;
  V<float> vFloat;

  vDouble = vFloat; //Can be compiled

  return 0;
}

Ответы [ 3 ]

3 голосов
/ 17 октября 2019

Во-первых, я бы предложил еще раз взглянуть на то, имеет ли смысл группировать этих членов. Например, возможно, что массив имеет смысл:

std::array<T, 8> data;

В этом случае проблема уменьшается, если вы можете сгруппировать элементы.

Если это нехороший путь, я боюсь, что до тех пор, пока в языке не появится статическое отражение, лучшее, что вы можете сделать, это что-то вроде [Boost.] PFR (ранее известный как magic_get), который пытается эмулировать статическое отражение через некоторыеумная и немного хакерская ( кашель попытка структурированного копирования с копированием). В C ++ 17:

template<typename OtherT>
S<T>& operator=(const S<OtherT>& other) {
    auto this_tuple = boost::pfr::structure_tie(*this);
    auto other_tuple = boost::pfr::structure_tie(other);
    this_tuple = other_tuple;

    return *this;
}

Обратите внимание, что, в частности, класс должен использоваться со структурированными привязками.

Если исключить их, лучшим вариантом, вероятно, будет либо написать все это, либо сделатьиспользование некоторых более мощных методов, основанных на макросах, которые позволяют объявлять или адаптировать класс таким образом, чтобы он стал отражаемым. Например, Boost.Hana и Boost.Fusion предоставляют такие возможности. Компромисс в том, что код будет значительно проще, особенно если вам нужна только эта особенность.

3 голосов
/ 17 октября 2019

Компилятор не может неявно сгенерировать оператор присваивания для двух разных типов. Вы должны определить это явно. Например

template<class T> struct S 
{
    T a,b,c,d,e,f,g,h;

    template <class U>
    S<T> & operator =( const S<U> &s )
    {
        a = T( s.a );
        b = T( s.b );
        c = T( s.c );
        d = T( s.d );
        e = T( s.e );
        f = T( s.f );
        g = T( s.g );
        h = T( s.h );

        return *this;
    }
};
1 голос
/ 17 октября 2019

Как показывают другие ответы, возможно добиться желаемого поведения (МНОГО) ускоренного метапрограммирования. Используя ответы на эти вопросы: Препроцессор C ++: избегать повторения кода в списке переменных членов , Итерировать по элементам Struct и Class * Я что-то скомбинировал, который перебирает все отражаемые элементы двух контейнеров и присваиваетих после кастинга.

Однако я не знаю, насколько надежен этот подход, и, вероятно, не рекомендовал бы использовать его в рабочем коде.

#include <boost/preprocessor.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/bind.hpp>

#define REM(...) __VA_ARGS__
#define EAT(...)

#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
#define STRIP(x) EAT x
#define PAIR(x) REM x

template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};

#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

// Custom code for struct assignment below here
struct dual_field_visitor
{
    template<class CA, class CB, class Visitor, class T>
    void operator()(CA & ca, CB & cb, Visitor v, T)
    {
        v(reflector::get_field_data<T::value>(ca), reflector::get_field_data<T::value>(cb));
    }
};

template<class CA, class CB, class Visitor>
void dual_visit_each(CA & ca, CB & cb, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<CA>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(dual_field_visitor(), boost::ref(ca), boost::ref(cb), v, _1));
}

struct assign_visitor
{
    template<class FieldDataA, class FieldDataB>
    void operator()(FieldDataA fa, FieldDataB fb)
    {
        fa.get() = static_cast<std::decay_t<decltype(fa.get())>>(fb.get());
    }
};

template<typename T>
struct Container
{
    REFLECTABLE
    (
        (T) a,
        (T) b
    )

    template<typename U>
    Container & operator=(const Container<U> & c)
    {
        dual_visit_each(*this, c, assign_visitor());
        return *this;
    }

    Container & operator=(const Container & c)
    {
        if(this != & c)
        {
            dual_visit_each(*this, c, assign_visitor());
        }
        return *this;
    }
};

int main()
{
    Container<int> a {1, 2};
    Container<float> b {3.0f, 4.0f};
    Container<float> c {5.0f, 6.0f};

    a = b;
    b = c;

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...