c++ - सुविधाजनक सी++ संरचना प्रारंभिकरण




struct initialization (7)

मैं 'पॉड' सी ++ structs शुरू करने के लिए एक सुविधाजनक तरीका खोजने की कोशिश कर रहा हूँ। अब, निम्नलिखित संरचना पर विचार करें:

struct FooBar {
  int foo;
  float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;

अगर मैं आसानी से सी (!) में इसे शुरू करना चाहता हूं, तो मैं बस लिख सकता हूं:

/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C

ध्यान दें कि मैं स्पष्ट रूप से निम्नलिखित नोटेशन से बचना चाहता हूं, क्योंकि अगर मैं भविष्य में संरचना में कुछ भी बदलता हूं तो यह मुझे अपनी गर्दन तोड़ने के लिए तैयार करता है:

/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?

/* A */ उदाहरण में सी ++ में समान (या कम से कम समान) प्राप्त करने के लिए, मुझे एक बेवकूफ कन्स्ट्रक्टर को कार्यान्वित करना होगा:

FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);

जो उबलते पानी के लिए अच्छा है, लेकिन आलसी लोगों के लिए उपयुक्त नहीं है (आलस्य एक अच्छी बात है, है ना?)। साथ ही, यह /* B */ उदाहरण के रूप में बहुत खराब है, क्योंकि यह स्पष्ट रूप से यह नहीं बताता कि कौन सा मूल्य किस सदस्य को जाता है।

तो, मेरा सवाल मूल रूप से मैं कैसे /* A */ या सी ++ में बेहतर कुछ हासिल कर सकता हूं? वैकल्पिक रूप से, मैं एक स्पष्टीकरण के साथ ठीक हूं कि मुझे ऐसा क्यों नहीं करना चाहिए (यानी मेरा मानसिक प्रतिमान बुरा क्यों है)।

संपादित करें

सुविधाजनक रूप से , मेरा मतलब है कि बनाए रखने योग्य और अनावश्यक भी


आप एक लैम्ब्डा का उपयोग कर सकते हैं:

const FooBar fb = [&] {
    FooBar fb;
    fb.foo = 12;
    fb.bar = 3.4;
    return fb;
}();

इस मुहावरे पर अधिक जानकारी हर्ब सटर के ब्लॉग पर पाई जा सकती है।


आपका प्रश्न कुछ हद तक मुश्किल है क्योंकि समारोह भी:

static FooBar MakeFooBar(int foo, float bar);

के रूप में कहा जा सकता है:

FooBar fb = MakeFooBar(3.4, 5);

अंतर्निहित संख्यात्मक प्रकारों के लिए पदोन्नति और रूपांतरण नियमों के कारण। (सी वास्तव में दृढ़ता से टाइप नहीं किया गया है)

सी ++ में, जो आप चाहते हैं वह प्राप्त करने योग्य है, हालांकि टेम्पलेट्स और स्थिर दावों की सहायता से:

template <typename Integer, typename Real>
FooBar MakeFooBar(Integer foo, Real bar) {
  static_assert(std::is_same<Integer, int>::value, "foo should be of type int");
  static_assert(std::is_same<Real, float>::value, "bar should be of type float");
  return { foo, bar };
}

सी में, आप पैरामीटर का नाम दे सकते हैं, लेकिन आप आगे कभी नहीं मिलेगा।

दूसरी ओर, यदि आप चाहते हैं कि सभी पैरामीटर नामित हैं, तो आप बहुत बोझिल कोड लिखते हैं:

struct FooBarMaker {
  FooBarMaker(int f): _f(f) {}
  FooBar Bar(float b) const { return FooBar(_f, b); }
  int _f;
};

static FooBarMaker Foo(int f) { return FooBarMaker(f); }

// Usage
FooBar fb = Foo(5).Bar(3.4);

और यदि आप चाहें तो आप प्रकार के पदोन्नति संरक्षण में काली मिर्च कर सकते हैं।


उन योगदानों में योगदानकर्ताओं को निकालें जो उनका वर्णन करते हैं (मूलभूत रिफैक्टरिंग):

FooBar fb = { foo(), bar() };

मुझे पता है कि शैली उस व्यक्ति के बहुत करीब है जिसका आप उपयोग नहीं करना चाहते थे, लेकिन यह निरंतर मूल्यों के आसान प्रतिस्थापन को सक्षम बनाता है और उन्हें समझाता है (इस प्रकार टिप्पणियों को संपादित करने की आवश्यकता नहीं है), अगर वे कभी भी बदलते हैं।

एक और चीज जो आप कर सकते हैं (चूंकि आप आलसी हैं) कन्स्ट्रक्टर इनलाइन बनाने के लिए है, इसलिए आपको उतना ही टाइप करने की आवश्यकता नहीं है ("Foobar ::" को हटाकर और एच और सीपीपी फ़ाइल के बीच स्विच करने में समय बिताया गया है):

struct FooBar {
  FooBar(int f, float b) : foo(f), bar(b) {}
  int foo;
  float bar;
};

कई कंपाइलर्स 'सी ++ फ्रंटेंड्स (जीसीसी और क्लैंग समेत) सी प्रारंभिक वाक्यविन्यास को समझते हैं। यदि आप कर सकते हैं, तो बस उस विधि का उपयोग करें।


फिर भी सी ++ में एक और तरीका है

struct Point
{
private:

 int x;
 int y;

public:
    Point& setX(int xIn) { x = Xin; return *this;}
    Point& setY(int yIn) { y = Yin; return *this;}

}

Point pt;
pt.setX(20).setY(20);

मुझे पता है कि यह सवाल पुराना है, लेकिन इसे हल करने का एक तरीका है जब तक कि C ++ 20 अंततः इस सुविधा को सी से सी ++ तक लाता है। इसे हल करने के लिए आप क्या कर सकते हैं, प्रारंभिक_सर्ट के साथ प्रीप्रोसेसर मैक्रोज़ का उपयोग अपने प्रारंभिक सत्यापन को सत्यापित करने के लिए किया जाता है। (मुझे पता है मैक्रोज़ आम तौर पर खराब होते हैं, लेकिन यहां मुझे एक और तरीका नहीं दिख रहा है।) नीचे उदाहरण कोड देखें:

#define INVALID_STRUCT_ERROR "Instantiation of struct failed: Type, order or number of attributes is wrong."

#define CREATE_STRUCT_1(type, identifier, m_1, p_1) \
{ p_1 };\
static_assert(offsetof(type, m_1) == 0, INVALID_STRUCT_ERROR);\

#define CREATE_STRUCT_2(type, identifier, m_1, p_1, m_2, p_2) \
{ p_1, p_2 };\
static_assert(offsetof(type, m_1) == 0, INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_2) >= sizeof(identifier.m_1), INVALID_STRUCT_ERROR);\

#define CREATE_STRUCT_3(type, identifier, m_1, p_1, m_2, p_2, m_3, p_3) \
{ p_1, p_2, p_3 };\
static_assert(offsetof(type, m_1) == 0, INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_2) >= sizeof(identifier.m_1), INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_3) >= (offsetof(type, m_2) + sizeof(identifier.m_2)), INVALID_STRUCT_ERROR);\

#define CREATE_STRUCT_4(type, identifier, m_1, p_1, m_2, p_2, m_3, p_3, m_4, p_4) \
{ p_1, p_2, p_3, p_4 };\
static_assert(offsetof(type, m_1) == 0, INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_2) >= sizeof(identifier.m_1), INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_3) >= (offsetof(type, m_2) + sizeof(identifier.m_2)), INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_4) >= (offsetof(type, m_3) + sizeof(identifier.m_3)), INVALID_STRUCT_ERROR);\

// Create more macros for structs with more attributes...

फिर जब आपके पास कॉन्स विशेषता वाले स्ट्रक्चर होते हैं, तो आप यह कर सकते हैं:

struct MyStruct
{
    const int attr1;
    const float attr2;
    const double attr3;
};

const MyStruct test = CREATE_STRUCT_3(MyStruct, test, attr1, 1, attr2, 2.f, attr3, 3.);

यह थोड़ा असुविधाजनक है, क्योंकि आपको प्रत्येक संभावित संख्या के गुणों के लिए मैक्रोज़ की आवश्यकता है और आपको मैक्रो कॉल में अपने उदाहरण के प्रकार और नाम को दोहराने की आवश्यकता है। इसके अलावा आप वापसी विवरण में मैक्रो का उपयोग नहीं कर सकते हैं, क्योंकि शुरुआत के बाद आवेषण आते हैं।

लेकिन यह आपकी समस्या का समाधान करता है: जब आप संरचना बदलते हैं, तो कॉल संकलन-समय पर विफल हो जाएगी।

यदि आप सी ++ 17 का उपयोग करते हैं, तो आप इन मैक्रोज़ को उसी प्रकार को मजबूर कर भी अधिक सख्त बना सकते हैं, उदाहरण के लिए:

#define CREATE_STRUCT_3(type, identifier, m_1, p_1, m_2, p_2, m_3, p_3) \
{ p_1, p_2, p_3 };\
static_assert(offsetof(type, m_1) == 0, INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_2) >= sizeof(identifier.m_1), INVALID_STRUCT_ERROR);\
static_assert(offsetof(type, m_3) >= (offsetof(type, m_2) + sizeof(identifier.m_2)), INVALID_STRUCT_ERROR);\
static_assert(typeid(p_1) == typeid(identifier.m_1), INVALID_STRUCT_ERROR);\
static_assert(typeid(p_2) == typeid(identifier.m_2), INVALID_STRUCT_ERROR);\
static_assert(typeid(p_3) == typeid(identifier.m_3), INVALID_STRUCT_ERROR);\

सी ++ में रास्ता /* B */ ठीक है, सी ++ 0x सिंटैक्स का विस्तार करने जा रहा है, इसलिए यह सी ++ कंटेनरों के लिए भी उपयोगी है। मुझे समझ में नहीं आता कि आप इसे बुरी शैली क्यों कहते हैं?

यदि आप नामों के साथ पैरामीटर इंगित करना चाहते हैं तो आप बूस्ट पैरामीटर लाइब्रेरी का उपयोग कर सकते हैं, लेकिन यह किसी से अपरिचित किसी को भ्रमित कर सकता है।

पुनर्वितरण संरचना सदस्य रीडरिंग फ़ंक्शन पैरामीटर की तरह हैं, ऐसे रिफैक्टरिंग समस्याएं पैदा कर सकती हैं यदि आप इसे बहुत सावधानी से नहीं करते हैं।





initialization