Какой вариант лучше для обхода полей класса

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Din666
Сообщения: 51
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

03 ноя 2016, 14:19

Думаю многим было бы интересно реализовать структуру/класс, в котором можно было бы обращаться к членам класса напрямую, но и при необходимости
циклом обойти их, чтобы сделать какие-то действия.

Код: Выделить всё

// нужен обход полей класса, объект класса должен быть копируемый и с move семантикой
// какой вариант с member pointer лучше по красоте, поддержке, скорости ....
// а может есть какой-нить третий ?

#include <string>
#include <unordered_map>
#include <iostream>
#include <functional>

struct Iface {
    virtual void print() const = 0;
    virtual ~Iface() {}
};

template <class T> struct Realize final : public Iface {
    Realize(
          const std::string & name
        , const T & val = T()
    ) : name(name), val(val) {}

    std::string name; // < исправлено - удалено const std::string name;
    T val;

    inline void print() const override {
        std::cout << "name=" << name << " val=" << val << std::endl;
    }
};

struct Usage {

    #define VAR(name, default_value) m_##name(#name, default_value)

    Usage() : VAR(Var1, 123), VAR(Var2, "test") {}

    Realize<int> m_Var1;
    Realize<std::string> m_Var2;

    // 1 вариант
    static const std::unordered_map<std::string, const Iface Usage::*> m_allVars1;

    inline void print1() const {
        for (const auto & each : m_allVars1) { (this->*(each.second)).print(); }
    }

    // 2 вариант
    using getMember = const Iface & (* const)(const Usage &);
    static const std::unordered_map<std::string, getMember> m_allVars2;

    inline void print2() const {
        for (const auto & each : m_allVars2) { each.second(*this).print(); }
    }
};

// 1 вариант
const decltype(Usage::m_allVars1) Usage::m_allVars1 = {

//     { "Var1", &Usage::m_Var1 } // error: could not convert ‘{&Usage::m_Var1}’ from ‘<brace-enclosed initializer list>’ to ‘const std::vector<Iface Usage::*>’

#define ELEMENT(name) { #name, reinterpret_cast<Iface Usage::*>(&Usage::m_##name) } // OK но так стремно

      ELEMENT(Var1)
    , ELEMENT(Var2)
};

// 2 вариант
const decltype(Usage::m_allVars2) Usage::m_allVars2 = {
#undef ELEMENT // )))
#define ELEMENT(name) { #name, [](const Usage &s) -> const Iface & {return s.m_##name;} }

      ELEMENT(Var1)
    , ELEMENT(Var2)
};

int main() {
    Usage usage;
    usage.print1();
    usage.print2();
    return 0;
}
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

03 ноя 2016, 17:33

Использование unordered_map неоправдано, так как мы нигде не используем поиск. Да и с типами ты что-то намудрил: как по-мне можно сделать всё проще.

Вот мой вариант, набросанный на скорую руку.

Код: Выделить всё

#include <iostream>
#include <vector>

namespace meta
{

struct FieldBase
{
   virtual void Print() const = 0;
};

class StructBase;

template <class Type>
class Field : public FieldBase
{
public:
   Field(const char* name, StructBase* strct);

   virtual void Print() const override;

   Field& operator=(const Type& value);
   operator Type() const;
   Type* operator &() const;

private:
   const char* m_name;
   Type m_value;
};

class StructBase
{
public:
   StructBase();
   void AddField(const FieldBase* field);
   void Print() const;

private:
   std::vector<const FieldBase*> m_fields;
};

template <class Type>
Field<Type>::Field(const char* name, StructBase* strct) : 
   m_name(name), m_value()
{
   strct->AddField(this);
}

template <class Type>
void Field<Type>::Print() const
{
   std::cout << "name=" << m_name << " value=" << m_value << std::endl;
}

template <class Type>
Field<Type>& Field<Type>: :o perator=(const Type& value)
{
   m_value = value;
   return *this;
}

template <class Type>
Field<Type>: :o perator Type() const
{
   return m_value;
}

template <class Type>
Type* Field<Type>: :o perator &() const
{
   return &m_value;
}

StructBase::StructBase()
{
}

void StructBase::AddField(const FieldBase* field)
{
   m_fields.push_back(field);
}

void StructBase::Print() const
{
   for (auto field : m_fields)
   {
      field->Print();
   }
}

} // namespace meta

#define DECLARE_STRUCT_BEGIN(NAME) struct NAME : public meta::StructBase {
#define DECLARE_STRUCT_END };
#define DECLARE_STRUCT_CONSTRUCTION_BEGIN(NAME) NAME() : meta::StructBase()
#define DECLARE_STRUCT_CONSTRUCTION_END {}
#define DECLARE_FIELD_CONSTRUCTION(NAME) , m_##NAME(#NAME, this)
#define DECLARE_FIELD(NAME, TYPE) meta::Field< TYPE > m_##NAME;

///////////////////////////////////////////////////////////////////////////

DECLARE_STRUCT_BEGIN(MyStruct)
   DECLARE_STRUCT_CONSTRUCTION_BEGIN(MyStruct)
      DECLARE_FIELD_CONSTRUCTION(Var1)
      DECLARE_FIELD_CONSTRUCTION(Var2)
   DECLARE_STRUCT_CONSTRUCTION_END

   DECLARE_FIELD(Var1, int)
   DECLARE_FIELD(Var2, std::string)
DECLARE_STRUCT_END

int main()
{
   MyStruct my;

   my.m_Var1 = 5;
   my.m_Var2 = "Test";

   my.Print();

   return 0;
}
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Din666
Сообщения: 51
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

07 ноя 2016, 10:40

Спасибо за ответ. В данном контексте ты прав насчет unordered_map, но на самом деле поиск по строковому типу нужен - это система конфига, нужен поиск и прямое обращение к полям класса и многое чего другое (в том числе все операторы из твоего варианта). Я для простоты не стал это показывать, чтобы была видна основная проблема. Насчет мув семантики у меня ошибка - 21 срока const std::string name; - не надо конст. А ты действительно считаешь оправданным такое количество макросов в твоем варианте? А самое главное у тебя нехватает конструктора копирования и мув, оператора присваивания. При копировании указатели в векторе станут невалидные, т.к. это не смещения относительно структуры класса, а просто внешние указатели.
std::vector<const FieldBase*> m_fields - неверно; надо std::vector<const FieldBase StructBase::*> m_fields;
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

07 ноя 2016, 21:46

- Даже если поиск нужен, я бы всё равно использовал либо вектор пар, либо в крайнем случае std::map. Использование unordered_map ведёт к чудовищному оверхеду и оправдано только на больших массивах данных. Полей же в структуре будет ну десяток, ну два десятка от силы. При таких количествах обычно даже map не используют, а обходятся вектором пар, так как перебор даёт всего на несколько итераций больше, чем бинарный поиск, в то время как на построении контейнера мы теряем все мизерные бонусы, которые заработали на поиске, да ещё и уходим в минус, причём прилично.

- В твоём коде в любом случае нужно убирать const у std::string, так как не будет работать не только мув семантика, но и оператор присваивания. По логике и так понятно, что name должен изменяться. Снимай const и прячь его в private, если не хочешь, чтобы его менял кто-то снаружи.

- Да, то, что структура должна копироваться я не учитывал, причём обдуманно. Причина та же, что и у тебя с unordered_map: хотел показать сам принцип, не загружая всё очевидным кодом, в котором суть утонет. Да, для меня код конструкторов и оператора присваивания очевиден - мог бы не объяснять, как работает shallow copy.

- Макросы не обязательны, но, как по мне, вполне оправданы. Они позволяют не делать одни и те же вещи снова и снова, а так же берегут от неправильного использования и опечаток (например, без использования макроса можно указать имя переменной одно, а name другой, а с макросом не получится).

- На счёт последнего исправления я вообще не понял, зачем оно нужно. У меня всё работает именно так, как я написал. Более того, я не знаю, что этот синтаксис обозначает (использование оператора доступа для звёздочки).
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

08 ноя 2016, 00:07

Полный вариант со всеми конструкторами и операторами и тестами на них.

Код: Выделить всё

#include <iostream>
#include <vector>
 
namespace meta
{
 
struct FieldBase
{
   virtual void Print() const = 0;
};
 
class StructBase;
 
template <class Type>
class Field : public FieldBase
{
public:
   Field(const Field& rhs) = delete;
   Field(Field&& rhs) = delete;
   Field& operator=(const Field& rhs) = delete;
   Field& operator=(Field&& rhs) = delete;

   Field(const char* name, StructBase* strct);
   Field(const Field& rhs, StructBase* strct);
   Field(Field&& rhs, StructBase* strct);

   void Assign(const Field& rhs, StructBase* strct);
   void Assign(Field&& rhs, StructBase* strct);
 
   Field& operator=(const Type& value);
   operator Type() const;
   Type* Addr() const;

   // FieldBase overrides
   virtual void Print() const override;
 
private:
   const char* m_name;
   Type m_value;
};
 
class StructBase
{
public:
   StructBase();

   StructBase(const StructBase& rhs) = delete;
   StructBase(StructBase&& rhs) = delete;
   StructBase& operator=(const StructBase& rhs) = delete;
   StructBase& operator=(StructBase&& rhs) = delete;

   void AddField(const FieldBase* field);
   void ClearFields();

   void Print() const;
 
private:
   std::vector<const FieldBase*> m_fields;
};
 
template <class Type>
Field<Type>::Field(const char* name, StructBase* strct) :
   m_name(name), m_value()
{
   strct->AddField(this);
}

template <class Type>
Field<Type>::Field(const Field<Type>& rhs, StructBase* strct) :
   m_name(rhs.m_name), m_value(rhs.m_value)
{
   strct->AddField(this);
}

template <class Type>
Field<Type>::Field(Field<Type>&& rhs, StructBase* strct) :
   m_name(rhs.m_name), m_value(std::move(rhs.m_value))
{
   strct->AddField(this);
   rhs.m_name = nullptr;
}

template <class Type>
void Field<Type>::Assign(const Field<Type>& rhs, StructBase* strct)
{
   if (&rhs != this)
   {
      m_name = rhs.m_name;
      m_value = rhs.m_value;
      strct->AddField(this);
   }
}

template <class Type>
void Field<Type>::Assign(Field<Type>&& rhs, StructBase* strct)
{
   if (&rhs != this)
   {
      m_name = rhs.m_name;
      rhs.m_name = nullptr;
      m_value = std::move(rhs.m_value);
      strct->AddField(this);
   }
}

template <class Type>
Field<Type>& Field<Type>: :o perator=(const Type& value)
{
   m_value = value;
   return *this;
}
 
template <class Type>
Field<Type>: :o perator Type() const
{
   return m_value;
}
 
template <class Type>
Type* Field<Type>::Addr() const
{
   return &m_value;
}

template <class Type>
void Field<Type>::Print() const
{
   std::cout << "name=" << m_name << " value=" << m_value << std::endl;
}
 
StructBase::StructBase() : m_fields()
{
}
 
void StructBase::AddField(const FieldBase* field)
{
   m_fields.push_back(field);
}

void StructBase::ClearFields()
{
   m_fields.clear();
}
 
void StructBase::Print() const
{
   for (auto field : m_fields)
   {
      field->Print();
   }
}
 
} // namespace meta
 
#define DECLARE_STRUCT_BEGIN(NAME) struct NAME : public meta::StructBase {
#define DECLARE_STRUCT_END };
#define DECLARE_STRUCT_CONSTRUCTION_BEGIN(NAME) NAME() : meta::StructBase()
#define DECLARE_STRUCT_CONSTRUCTION_END {}
#define DECLARE_STRUCT_COPYING_BEGIN(NAME) NAME(const NAME& rhs) : meta::StructBase()
#define DECLARE_STRUCT_COPYING_END {}
#define DECLARE_STRUCT_MOVE_COPYING_BEGIN(NAME) NAME(NAME&& rhs) : meta::StructBase()
#define DECLARE_STRUCT_MOVE_COPYING_END { rhs.ClearFields(); }
#define DECLARE_STRUCT_ASSIGNMENT_BEGIN(NAME) NAME& operator=(const NAME& rhs) { if (&rhs != this) { ClearFields();
#define DECLARE_STRUCT_ASSIGNMENT_END } }
#define DECLARE_STRUCT_MOVE_ASSIGNMENT_BEGIN(NAME) NAME& operator=(NAME&& rhs) { if (&rhs != this) { ClearFields();
#define DECLARE_STRUCT_MOVE_ASSIGNMENT_END rhs.ClearFields(); } }

#define DECLARE_FIELD_CONSTRUCTION(NAME) , m_##NAME(#NAME, this)
#define DECLARE_FIELD_COPYING(NAME) , m_##NAME(rhs.m_##NAME, this)
#define DECLARE_FIELD_MOVE_COPYING(NAME) , m_##NAME(std::move(rhs.m_##NAME), this)
#define DECLARE_FIELD_ASSIGNMENT(NAME) m_##NAME.Assign(rhs.m_##NAME, this);
#define DECLARE_FIELD_MOVE_ASSIGNMENT(NAME) m_##NAME.Assign(std::move(rhs.m_##NAME), this);
#define DECLARE_FIELD(NAME, TYPE) meta::Field< TYPE > m_##NAME;
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

08 ноя 2016, 00:07

Код: Выделить всё

DECLARE_STRUCT_BEGIN(MyStruct)
   DECLARE_STRUCT_CONSTRUCTION_BEGIN(MyStruct)
      DECLARE_FIELD_CONSTRUCTION(Var1)
      DECLARE_FIELD_CONSTRUCTION(Var2)
   DECLARE_STRUCT_CONSTRUCTION_END

   DECLARE_STRUCT_COPYING_BEGIN(MyStruct)
      DECLARE_FIELD_COPYING(Var1)
      DECLARE_FIELD_COPYING(Var2)
   DECLARE_STRUCT_COPYING_END

   DECLARE_STRUCT_MOVE_COPYING_BEGIN(MyStruct)
      DECLARE_FIELD_MOVE_COPYING(Var1)
      DECLARE_FIELD_MOVE_COPYING(Var2)
   DECLARE_STRUCT_MOVE_COPYING_END

   DECLARE_STRUCT_ASSIGNMENT_BEGIN(MyStruct)
      DECLARE_FIELD_ASSIGNMENT(Var1)
      DECLARE_FIELD_ASSIGNMENT(Var2)
   DECLARE_STRUCT_ASSIGNMENT_END

   DECLARE_STRUCT_MOVE_ASSIGNMENT_BEGIN(MyStruct)
      DECLARE_FIELD_MOVE_ASSIGNMENT(Var1)
      DECLARE_FIELD_MOVE_ASSIGNMENT(Var2)
   DECLARE_STRUCT_MOVE_ASSIGNMENT_END
 
   DECLARE_FIELD(Var1, int)
   DECLARE_FIELD(Var2, std::string)
DECLARE_STRUCT_END
 
int main()
{
   MyStruct my;
   my.m_Var1 = 5;
   my.m_Var2 = "Test";
   std::cout << "Structure my:" << std::endl;
   my.Print();
   std::cout << std::endl;

   MyStruct my2 = my;
   std::cout << "Structure my copied to my2." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << std::endl;

   MyStruct my3 = std::move(my2);
   std::cout << "Structure my2 moved to my3." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << "Structure my3:" << std::endl;
   my3.Print();
   std::cout << std::endl;

   MyStruct another_my;
   another_my.m_Var1 = 10;
   another_my.m_Var2 = "Foo";
   std::cout << "Structure another_my:" << std::endl;
   another_my.Print();
   std::cout << std::endl;

   my2 = another_my;
   std::cout << "Structure another_my assgined to my2." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << std::endl;

   my3 = std::move(my2);
   std::cout << "Structure my2 moved by assignment to my3." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << "Structure my3:" << std::endl;
   my3.Print();
   std::cout << std::endl; 

   return 0;
}
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Din666
Сообщения: 51
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

08 ноя 2016, 10:52

Думаю совместить оба решения и получится конфетка. В моем варианте хорошо что мне не надо копировать каждый раз static const std::unordered_map<std::string, const Iface Usage::*> m_allVars1; - внимание статик конст !!! ))). а в твоем варианте хорошие макросы, но много лишнего кода. конструкторы(copy,mov),и op= создаются автоматом если не делать руками хотябы один, а у меня переменные с одинаковыми названиями раскиданы по коду, что не есть хорошо

>> - На счёт последнего исправления я вообще не понял, зачем оно нужно. ........ Более того, я не знаю, что этот синтаксис обозначает

это указатель на поле класса
то есть смещение относительно начала класса, которое постоянное в скомпилированном объекте, это и можно использоваться для инита один раз
http://en.cppreference.com/w/cpp/language/pointer
кратко:

Код: Выделить всё

struct C { int m; };
main {
int C::* p = &C::m;          // pointer to data member m of class C
C c = {7};
std::cout << c.*p << '\n';   // prints 7
}
Спасибо !!
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

08 ноя 2016, 17:11

Я что-то не сообразил сразу использовать указатель на мембер. Если честно, я вообще не вспомнил, что так можно делать. Вполне возможно, что это действительно упростит код код. Вечерком обдумаю всё и выложу код, максимально оптимальный и лаконичный с моей точки зрения. Потом и "сверим часы" :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Romeo
Сообщения: 3091
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

09 ноя 2016, 00:59

В общем, вот окончательный вариант, который кажется мне самым оптимальным/кратким.

Код: Выделить всё

#include <iostream>
#include <vector>
#include <string>
 
namespace meta
{
 
struct FieldBase
{
   virtual void Print() const = 0;
};
 
template <class Type>
class Field : public FieldBase
{
public:
   Field(const char* name);
   Field(const char* name, const Type& value);
 
   Field& operator=(const Type& value);
   Field& operator=(Type&& value);
   operator Type() const;
   Type* operator&() const;

   // FieldBase overrides
   virtual void Print() const override;
 
private:
   const char* m_name;
   Type m_value;
};

template <class Type>
Field<Type>::Field(const char* name) :
   m_name(name), m_value()
{
}

template <class Type>
Field<Type>::Field(const char* name, const Type& value) : 
   m_name(name), m_value(value)
{
}

template <class Type>
Field<Type>& Field<Type>: :o perator=(const Type& value)
{
   m_value = value;
   return *this;
}
 
template <class Type>
Field<Type>& Field<Type>: :o perator=(Type&& value)
{
   m_value = std::move(value);
   return *this;
}

template <class Type>
Field<Type>: :o perator Type() const
{
   return m_value;
}
 
template <class Type>
Type* Field<Type>: :o perator&() const
{
   return &m_value;
}

template <class Type>
void Field<Type>::Print() const
{
   std::cout << "name=" << m_name << " value=" << m_value << std::endl;
}

// Fake structure to avoid syntax error in constructor of an user structure.
class StructBase {};

} // namespace meta
 
#define DECLARE_STRUCT_BEGIN(NAME) struct NAME : public meta::StructBase { \
private: \
   static const meta::FieldBase NAME::* sm_field_ptrs_array[]; \
 \
public: \
   void Print() const;

#define DECLARE_STRUCT_END };

#define DECLARE_STRUCT_CONSTRUCTION_BEGIN(NAME) NAME() : meta::StructBase()
#define DECLARE_STRUCT_CONSTRUCTION_END {}

#define DECLARE_FIELDS_MAPING_BEGIN(NAME) \
const meta::FieldBase NAME::* NAME::sm_field_ptrs_array[] = \
{

#define DECLARE_FIELD_MAPPING(STRUCT_NAME, FIELD_NAME) \
   reinterpret_cast<meta::FieldBase STRUCT_NAME::*>(&STRUCT_NAME::m_##FIELD_NAME),

#define DECLARE_FIELDS_MAPING_END(NAME) \
}; \
 \
void NAME::Print() const \
{ \
   const auto size = sizeof(sm_field_ptrs_array)/sizeof(*sm_field_ptrs_array); \
   for (auto i = 0; i < size; ++i) \
   { \
      (this->*sm_field_ptrs_array[i]).Print(); \
   } \
}

#define DECLARE_FIELD_CONSTRUCTION(NAME) , m_##NAME(#NAME)
#define DECLARE_FIELD_CONSTRUCTION_DEF(NAME, VALUE) , m_##NAME(#NAME, VALUE)
#define DECLARE_FIELD(NAME, TYPE) meta::Field< TYPE > m_##NAME;
 
///////////////////////////////////////////////////////////////////////////
 
DECLARE_STRUCT_BEGIN(MyStruct)
   DECLARE_STRUCT_CONSTRUCTION_BEGIN(MyStruct)
      DECLARE_FIELD_CONSTRUCTION(Var1)
      DECLARE_FIELD_CONSTRUCTION(Var2)
   DECLARE_STRUCT_CONSTRUCTION_END

   DECLARE_FIELD(Var1, int)
   DECLARE_FIELD(Var2, std::string)
DECLARE_STRUCT_END

DECLARE_FIELDS_MAPING_BEGIN(MyStruct)
   DECLARE_FIELD_MAPPING(MyStruct, Var1)
   DECLARE_FIELD_MAPPING(MyStruct, Var2)
DECLARE_FIELDS_MAPING_END(MyStruct)
 
int main()
{
   MyStruct my;
   my.m_Var1 = 5;
   my.m_Var2 = std::string("Test");
   std::cout << "Structure my:" << std::endl;
   my.Print();
   std::cout << std::endl;

   MyStruct my2 = my;
   std::cout << "Structure my copied to my2." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << std::endl;

   MyStruct my3 = std::move(my2);
   std::cout << "Structure my2 moved to my3." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << "Structure my3:" << std::endl;
   my3.Print();
   std::cout << std::endl;

   MyStruct another_my;
   another_my.m_Var1 = 10;
   another_my.m_Var2 = std::string("Foo");
   std::cout << "Structure another_my:" << std::endl;
   another_my.Print();
   std::cout << std::endl;

   my2 = another_my;
   std::cout << "Structure another_my assgined to my2." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << std::endl;

   my3 = std::move(my2);
   std::cout << "Structure my2 moved by assignment to my3." << std::endl;
   std::cout << "Structure my2:" << std::endl;
   my2.Print();
   std::cout << "Structure my3:" << std::endl;
   my3.Print();
   std::cout << std::endl; 

   return 0;
}
В качестве итога скажу следующее. Использование pointer-to-member действительно позволяет сильно упростить код. В первую очередь это позволяет избавиться от контейнера со ссылками/указателями на поля в объекте, заменив одним статическим контейнером на каждую пользовательскую структуру. Удаление контейнера из объекта влечёт за собой упрощение стандартных конструкторов/операторов до уровня генерируемых, так что нужда в их ручной имплементации отпадает, что ещё больше сжимает код.

P.S. А вот от использования unordered_map я бы всё-таки рекомендовал тебе уйти. Рассуждения о причинах приведены несколькими сообщениями выше.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Din666
Сообщения: 51
Зарегистрирован: 17 июл 2015, 13:25
Откуда: Moscow
Контактная информация:

09 ноя 2016, 11:00

OK. насчет unordered_map буду иметь ввиду когда придется делать много копирований контейнера. А что скажешь насчет второго моего варианта на лямддах?
В продакшн коде я выбрал именно вариант с лямбдами, чтобы без реинтерпрет_каста.
кстати Field<Type>: :o perator Type() const ---> Field<Type>: :o perator Type&() const - чтобы не было копирования - конст же все равно, + virtual ~FieldBase() {}
// вроде интересная тема, странно что больше никто не участвует ((
Ответить