การเริ่มต้นโครงสร้าง C ++ ที่สะดวก


145

ฉันกำลังพยายามหาวิธีที่สะดวกในการเริ่มต้นโครงสร้าง C ++ 'pod' ตอนนี้พิจารณาโครงสร้างต่อไปนี้:

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

หากฉันต้องการเริ่มต้นสิ่งนี้อย่างสะดวกใน C (!) ฉันสามารถเขียน:

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

โปรดทราบว่าฉันต้องการหลีกเลี่ยงสัญกรณ์ต่อไปนี้อย่างชัดเจนเพราะมันทำให้ฉันรู้สึกว่าถูกทำให้คอหักถ้าฉันเปลี่ยนแปลงอะไรในโครงสร้างในอนาคต:

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

เพื่อให้บรรลุสิ่งเดียวกัน (หรืออย่างน้อยก็คล้ายกัน) ใน C ++ ดังใน/* A */ตัวอย่างฉันจะต้องใช้ตัวสร้างงี่เง่า:

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

ซึ่งดีสำหรับการต้มน้ำ แต่ไม่เหมาะสำหรับคนขี้เกียจ (ความขี้เกียจเป็นสิ่งที่ดีใช่มั้ย?) นอกจากนี้มันค่อนข้างแย่พอ ๆ กับ/* B */ตัวอย่างเนื่องจากไม่ได้ระบุอย่างชัดเจนว่าค่าใดไปที่สมาชิก

ดังนั้นคำถามของฉันโดยพื้นฐานแล้วฉันจะบรรลุสิ่งที่คล้าย/* A */หรือดีกว่าใน C ++ ได้อย่างไร อีกวิธีหนึ่งฉันจะโอเคกับคำอธิบายว่าทำไมฉันไม่ควรทำสิ่งนี้ (เช่นทำไมกระบวนทัศน์ทางจิตของฉันไม่ดี)

แก้ไข

ตามสะดวกผมหมายถึงยังบำรุงรักษาและการที่ไม่ซ้ำซ้อน


2
ผมคิดว่าตัวอย่าง B ใกล้เคียงกับที่คุณจะได้รับ
Marlon

2
ฉันไม่เห็นว่าตัวอย่าง B เป็น "สไตล์ที่ไม่ดี" อย่างไร มันสมเหตุสมผลสำหรับฉันเนื่องจากคุณกำลังเริ่มต้นสมาชิกแต่ละคนตามค่านิยมของพวกเขา
Mike Bailey

27
ไมค์มันเป็นลักษณะที่ไม่ดีเพราะมันไม่ชัดเจนว่าค่าใดไปที่สมาชิกคนไหน คุณต้องไปดูนิยามของโครงสร้างแล้วนับสมาชิกเพื่อค้นหาว่าแต่ละค่าหมายถึงอะไร
jnnnnn

9
นอกจากนี้หากคำจำกัดความของ FooBar มีการเปลี่ยนแปลงในอนาคตการเริ่มต้นอาจใช้งานไม่ได้
Edward Falk

หากการเริ่มต้นใช้เวลานานและซับซ้อนอย่าลืมเกี่ยวกับรูปแบบตัวสร้าง
เลื่อน

คำตอบ:


23

การกำหนดค่าเริ่มต้นที่กำหนดไว้จะได้รับการสนับสนุนใน c ++ 2a แต่คุณไม่ต้องรอเพราะGCC, Clang และ MSVC รองรับอย่างเป็นทางการ

#include <iostream>
#include <filesystem>

struct hello_world {
    const char* hello;
    const char* world;
};

int main () 
{
    hello_world hw = {
        .hello = "hello, ",
        .world = "world!"
    };

    std::cout << hw.hello << hw.world << std::endl;
    return 0;
}

GCC Demo MSVC Demo


Caveat emptor: โปรดทราบว่าหากคุณเพิ่มพารามิเตอร์ที่ส่วนท้ายของโครงสร้างในภายหลังการกำหนดค่าเริ่มต้นแบบเก่าจะยังคงคอมไพล์แบบเงียบ ๆ โดยไม่ได้รับการกำหนดค่าเริ่มต้น
Catskul

1
@Catskul ไม่มันจะเริ่มต้นด้วยรายการ initializer ว่างซึ่งจะทำให้เริ่มต้นด้วยศูนย์
ivaigult

คุณถูก. ขอบคุณ. ฉันควรชี้แจงพารามิเตอร์ที่เหลือจะถูกกำหนดค่าเริ่มต้นอย่างมีประสิทธิภาพ ประเด็นที่ฉันตั้งใจจะทำก็คือใครก็ตามที่หวังว่าสิ่งนี้จะช่วยบังคับใช้การเริ่มต้นประเภท POD อย่างชัดเจนจะต้องผิดหวัง
Catskul

43

ตั้งแต่ style Aไม่ได้รับอนุญาตใน C ++ และคุณไม่ต้องการstyle Bแล้ววิธีการใช้style BX:

FooBar fb = { /*.foo=*/ 12, /*.bar=*/ 3.4 };  // :)

อย่างน้อยก็ช่วยได้บ้าง


8
+1: ไม่แน่ใจว่ามีการเริ่มต้นที่ถูกต้อง (จากคอมไพเลอร์ POV) แต่แน่นอนว่าจะช่วยผู้อ่านได้ ... แม้ว่าความคิดเห็นควรได้รับการซิงค์
Matthieu M.

18
ความคิดเห็นไม่ได้ป้องกันการเริ่มต้นโครงสร้างไม่ให้เสียหายหากฉันแทรกฟิลด์ใหม่ระหว่างfooและbarในอนาคต C จะยังคงเริ่มต้นฟิลด์ที่เราต้องการ แต่ C ++ จะไม่ และนี่คือประเด็นของคำถาม - วิธีบรรลุผลลัพธ์เดียวกันใน C ++ ฉันหมายความว่า Python ทำสิ่งนี้กับอาร์กิวเมนต์ที่มีชื่อ C - ด้วยฟิลด์ "ชื่อ" และ C ++ ก็ควรมีบางอย่างเช่นกันฉันหวังว่า
dmitry_romanov

2
ความคิดเห็นในการซิงค์? ให้ฉันได้พัก. ความปลอดภัยผ่านหน้าต่าง จัดลำดับพารามิเตอร์และบูมใหม่ explicit FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) ดีมากกับ สังเกตคำหลักที่ชัดเจน แม้แต่การทำลายมาตรฐานก็ยังดีกว่าในเรื่องความปลอดภัย ใน Clang: -Wno-c99-extensions
Daniel O

@DanielW ไม่ได้เกี่ยวกับสิ่งที่ดีกว่าหรือสิ่งที่ไม่ใช่ คำตอบนี้เป็นไปตามที่ OP ไม่ต้องการสไตล์ A (ไม่ใช่ c ++), B หรือ C ซึ่งครอบคลุมกรณีที่ถูกต้องทั้งหมด
iammilind

@iammilind ฉันคิดว่าคำใบ้ว่าทำไมกระบวนทัศน์ทางจิตของ OP ถึงไม่ดีสามารถปรับปรุงคำตอบได้ ฉันคิดว่าสิ่งนี้อันตรายเหมือนตอนนี้
Daerst

11

คุณสามารถใช้แลมด้า:

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

ข้อมูลเพิ่มเติมเกี่ยวกับสำนวนนี้สามารถพบได้ในบล็อกสมุนไพรซัทเทอ


1
แนวทางดังกล่าวเริ่มต้นฟิลด์สองครั้ง เมื่ออยู่ในตัวสร้าง ประการที่สองคือfb.XXX = YYY.
Dmytro Ovdiienko

8

คำถามของคุณค่อนข้างยากเพราะแม้แต่ฟังก์ชั่น:

static FooBar MakeFooBar(int foo, float bar);

อาจเรียกได้ว่าเป็น:

FooBar fb = MakeFooBar(3.4, 5);

เนื่องจากกฎการส่งเสริมการขายและการแปลงสำหรับประเภทตัวเลขในตัว (C ไม่เคยพิมพ์รุนแรงจริงๆ)

ใน C ++ สิ่งที่คุณต้องการสามารถทำได้แม้ว่าด้วยความช่วยเหลือของเทมเพลตและการยืนยันแบบคงที่:

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 };
}

ในภาษา C คุณสามารถตั้งชื่อพารามิเตอร์ได้ แต่คุณจะไม่ไปต่อ

ในทางกลับกันหากสิ่งที่คุณต้องการคือชื่อพารามิเตอร์คุณต้องเขียนโค้ดที่ยุ่งยากมากมาย:

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);

และคุณสามารถพริกไทยในการป้องกันการส่งเสริมประเภทถ้าคุณต้องการ


1
"ใน C ++ สิ่งที่คุณต้องการคือทำได้": OP ไม่ได้ขอให้ช่วยป้องกันการผสมลำดับของพารามิเตอร์ใช่หรือไม่ เทมเพลตที่คุณเสนอจะบรรลุผลได้อย่างไร เพื่อความง่ายสมมติว่าเรามีพารามิเตอร์ 2 ตัวทั้งสองตัว int
สูงสุด

@max: จะป้องกันเฉพาะในกรณีที่ประเภทต่างกัน (แม้ว่าจะสามารถเปลี่ยนแปลงได้ซึ่งกันและกัน) ซึ่งเป็นสถานการณ์ OP หากไม่สามารถแยกแยะประเภทได้แน่นอนว่ามันใช้ไม่ได้ แต่นั่นเป็นคำถามอื่น ๆ ทั้งหมด
Matthieu M.

อ่าเข้าใจแล้ว ใช่นี่เป็นปัญหาสองประการที่แตกต่างกันและฉันเดาว่าปัญหาที่สองไม่มีวิธีแก้ปัญหาที่ดีใน C ++ ในขณะนี้ (แต่ดูเหมือนว่า C ++ 20 กำลังเพิ่มการสนับสนุนสำหรับชื่อพารามิเตอร์สไตล์ C99 ในการเริ่มต้นรวม)
สูงสุด

8

แยกสิ่งที่เจือปนออกเป็นฟังก์ชันที่อธิบายถึงสิ่งเหล่านี้ (การปรับโครงสร้างพื้นฐาน):

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

ฉันรู้ว่าสไตล์นั้นใกล้เคียงกับที่คุณไม่ต้องการใช้มาก แต่มันช่วยให้เปลี่ยนค่าคงที่ได้ง่ายขึ้นและยังอธิบายได้ (ไม่จำเป็นต้องแก้ไขความคิดเห็น) หากพวกเขาเปลี่ยนไป

อีกสิ่งหนึ่งที่คุณทำได้ (เนื่องจากคุณขี้เกียจ) คือทำให้ตัวสร้างเป็นแบบอินไลน์ดังนั้นคุณไม่จำเป็นต้องพิมพ์มากนัก (ลบ "Foobar ::" และเวลาที่ใช้ในการสลับระหว่างไฟล์ h และ cpp):

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

1
ฉันขอแนะนำให้ทุกคนที่อ่านคำถามนี้เลือกรูปแบบในข้อมูลโค้ดด้านล่างสำหรับคำตอบนี้หากสิ่งที่คุณต้องการทำคือสามารถเริ่มต้นโครงสร้างด้วยชุดค่าได้อย่างรวดเร็ว
kayleeFrye_onDeck

6

ส่วนหน้า C ++ ของคอมไพเลอร์จำนวนมาก (รวมถึง GCC และ clang) เข้าใจไวยากรณ์ C initializer ถ้าทำได้ก็ใช้วิธีนั้น


16
ซึ่งไม่เป็นไปตามมาตรฐาน C ++!
bitmask

5
ฉันรู้ว่ามันไม่ได้มาตรฐาน แต่ถ้าคุณสามารถใช้มันก็ยังคงเป็นวิธีที่เหมาะสมที่สุดในการเริ่มต้นโครงสร้าง
Matthias Urlichs

2
คุณสามารถป้องกันประเภทของ x และ y ที่ทำให้ตัวสร้างผิดเป็นส่วนตัว:private: FooBar(float x, int y) {};
dmitry_romanov

4
clang (คอมไพเลอร์ c ++ ที่ใช้ llvm) ยังสนับสนุนไวยากรณ์นี้ เสียดายที่ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน
nimrodm

เราทุกคนทราบดีว่า C initializers ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน C ++ แต่คอมไพเลอร์หลายคนเข้าใจและคำถามไม่ได้บอกว่าคอมไพเลอร์ใดถูกกำหนดเป้าหมายถ้ามี ดังนั้นโปรดอย่าลงคะแนนคำตอบนี้
Matthias Urlichs

4

อีกวิธีหนึ่งใน C ++ คือ

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);

2
ยุ่งยากสำหรับการเขียนโปรแกรมเชิงฟังก์ชัน (เช่นการสร้างวัตถุในรายการอาร์กิวเมนต์ของการเรียกใช้ฟังก์ชัน) แต่เป็นความคิดที่ดีจริงๆ!
bitmask

27
เครื่องมือเพิ่มประสิทธิภาพอาจลดได้ แต่ตาของฉันไม่ทำ
Matthieu M.

6
สองคำ: อ๊าก ... อ๊าก! วิธีนี้ดีกว่าการใช้ข้อมูลสาธารณะด้วย 'Point pt; pt.x = pt.y = 20; `? หรือถ้าคุณต้องการการห่อหุ้มสิ่งนี้จะดีกว่าตัวสร้างอย่างไร?
OldPeculier

3
มันดีกว่าคอนสตรัคเตอร์เพราะคุณต้องดูที่การประกาศคอนสตรัคเตอร์สำหรับลำดับพารามิเตอร์ ... มันคือ x, y หรือ y, x แต่วิธีที่ฉันแสดงให้เห็นชัดเจนที่ไซต์โทร
parapura rajkumar

2
สิ่งนี้ใช้ไม่ได้ถ้าคุณต้องการโครงสร้าง const หรือถ้าคุณต้องการบอกคอมไพลเลอร์ว่าไม่อนุญาตให้มีโครงสร้างที่ไม่ได้กำหนดค่าเริ่มต้น หากคุณจริงๆต้องการที่จะทำมันด้วยวิธีนี้อย่างน้อยทำเครื่องหมาย setters ด้วยinline!
Matthias Urlichs

3

ตัวเลือก D:

FooBar FooBarMake(int foo, float bar)

กฎหมาย C, กฎหมาย C ++ ปรับให้เหมาะสมสำหรับ POD ได้อย่างง่ายดาย แน่นอนว่าไม่มีอาร์กิวเมนต์ที่ตั้งชื่อ แต่ก็เหมือนกับ C ++ ทั้งหมด หากคุณต้องการอาร์กิวเมนต์ที่ตั้งชื่อ Objective C ควรเป็นทางเลือกที่ดีกว่า

ตัวเลือก E:

FooBar fb;
memset(&fb, 0, sizeof(FooBar));
fb.foo = 4;
fb.bar = 15.5f;

กฎหมาย C, กฎหมาย C ++ อาร์กิวเมนต์ที่ตั้งชื่อ


12
แทนที่จะใช้ memset คุณสามารถใช้FooBar fb = {};ใน C ++ ได้ค่าเริ่มต้นจะเริ่มต้นสมาชิกโครงสร้างทั้งหมด
Öö Tiib

@ ÖöTiib: น่าเสียดายที่ C ผิดกฎหมาย
CB Bailey

3

ฉันรู้ว่าคำถามนี้เก่า แต่มีวิธีแก้ปัญหานี้จนกระทั่งในที่สุด C ++ 20 ก็นำคุณสมบัตินี้จาก C เป็น C ++ สิ่งที่คุณสามารถทำได้เพื่อแก้ปัญหานี้คือใช้มาโครตัวประมวลผลล่วงหน้าที่มี static_asserts เพื่อตรวจสอบการเริ่มต้นของคุณว่าถูกต้อง (ฉันรู้ว่าโดยทั่วไปมาโครไม่ดี แต่ที่นี่ฉันไม่เห็นวิธีอื่น) ดูโค้ดตัวอย่างด้านล่าง:

#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.);

ไม่สะดวกเล็กน้อยเนื่องจากคุณต้องการมาโครสำหรับทุกแอตทริบิวต์ที่เป็นไปได้และคุณต้องทำซ้ำประเภทและชื่อของอินสแตนซ์ของคุณในการเรียกมาโคร นอกจากนี้คุณไม่สามารถใช้แมโครในคำสั่ง return เนื่องจากการยืนยันมาหลังจากการเริ่มต้น

แต่มันช่วยแก้ปัญหาของคุณได้: เมื่อคุณเปลี่ยนโครงสร้างการเรียกจะล้มเหลวในเวลาคอมไพล์

หากคุณใช้ C ++ 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);\

มีข้อเสนอ C ++ 20 เพื่ออนุญาตตัวเริ่มต้นที่มีชื่อหรือไม่?
Maël Nison


2

ทาง /* B */ดีใน C ++ นอกจากนี้ C ++ 0x จะขยายไวยากรณ์ดังนั้นจึงมีประโยชน์สำหรับคอนเทนเนอร์ C ++ ด้วย ฉันไม่เข้าใจว่าทำไมคุณถึงเรียกมันว่าสไตล์ไม่ดี?

หากคุณต้องการระบุพารามิเตอร์ด้วยชื่อคุณสามารถใช้ไลบรารีพารามิเตอร์บูสต์ แต่อาจทำให้คนที่ไม่คุ้นเคยกับมันสับสน

การเรียงลำดับสมาชิกของโครงสร้างใหม่ก็เหมือนกับการจัดลำดับพารามิเตอร์ของฟังก์ชันการปรับโครงสร้างใหม่อาจทำให้เกิดปัญหาได้หากคุณไม่ทำอย่างระมัดระวัง


7
ฉันเรียกมันว่ารูปแบบที่ไม่ดีเพราะฉันคิดว่ามันเป็นศูนย์ที่ดูแลรักษาได้ จะเกิดอะไรขึ้นถ้าฉันเพิ่มสมาชิกอีกในหนึ่งปี? หรือหากฉันเปลี่ยนลำดับ / ประเภทของสมาชิก? โค้ดทุกชิ้นที่เริ่มต้นมันอาจจะพัง (เป็นไปได้มาก)
bitmask

2
@bitmask แต่ตราบใดที่คุณไม่ได้ตั้งชื่ออาร์กิวเมนต์คุณจะต้องอัปเดตการเรียกตัวสร้างด้วยและฉันคิดว่ามีคนไม่มากที่คิดว่าคอนสตรัคเตอร์เป็นรูปแบบที่ไม่ดีที่ไม่สามารถเข้าถึงได้ ฉันยังคิดว่าการเริ่มต้นชื่อไม่ใช่ C แต่เป็น C99 ซึ่ง C ++ ไม่ใช่ซูเปอร์เซ็ตอย่างแน่นอน
Christian Rau

2
หากคุณเพิ่มสมาชิกคนอื่นในอีกหนึ่งปีจนสิ้นสุดโครงสร้างสมาชิกนั้นจะถูกกำหนดค่าเริ่มต้นในรหัสที่มีอยู่แล้ว หากคุณเรียงลำดับใหม่คุณจะต้องแก้ไขโค้ดที่มีอยู่ทั้งหมดก็ไม่ต้องทำอะไร
Öö Tiib

1
@bitmask: ตัวอย่างแรกก็จะเป็น "unmaintainable" เช่นกัน จะเกิดอะไรขึ้นถ้าคุณเปลี่ยนชื่อตัวแปรในโครงสร้างแทน แน่นอนคุณสามารถแทนที่ทั้งหมดได้ แต่อาจเปลี่ยนชื่อตัวแปรโดยไม่ได้ตั้งใจที่ไม่ควรเปลี่ยนชื่อ
Mike Bailey

@ChristianRau ตั้งแต่เมื่อไหร่ C99 ไม่ใช่ C? C กลุ่มและ C99 เป็นเวอร์ชันเฉพาะ / ข้อกำหนด ISO ไม่ใช่หรือ
altendky

1

ไวยากรณ์อะไรประมาณนี้

typedef struct
{
    int a;
    short b;
}
ABCD;

ABCD abc = { abc.a = 5, abc.b = 7 };

เพิ่งทดสอบบน Microsoft Visual C ++ 2015 และบน g ++ 6.0.2 ทำงานได้ดี
คุณสามารถสร้างมาโครเฉพาะได้เช่นกันหากคุณต้องการหลีกเลี่ยงการทำซ้ำชื่อตัวแปร


clang++3.5.0-10 -Weverything -std=c++1zดูเหมือนจะยืนยันว่า แต่มันดูไม่เหมาะสม คุณรู้หรือไม่ว่ามาตรฐานยืนยันว่านี่คือ C ++ ที่ถูกต้อง?
bitmask

ฉันไม่รู้ แต่ฉันใช้มันในคอมไพเลอร์ต่างกันเมื่อนานมาแล้วและไม่เห็นปัญหาใด ๆ ตอนนี้ทดสอบบน g ++ 4.4.7 - ใช้งานได้ดี
cls

5
ไม่คิดว่างานนี้ ลองABCD abc = { abc.b = 7, abc.a = 5 };.
raymai97

@deselect ใช้งานได้เนื่องจากฟิลด์เริ่มต้นด้วยค่าส่งคืนโดยตัวดำเนินการ = ดังนั้นคุณเริ่มต้นสมาชิกชั้นเรียนสองครั้ง
Dmytro Ovdiienko

1

สำหรับฉันวิธีที่เกียจคร้านที่สุดในการอนุญาตให้ใช้ inline inizialization คือใช้มาโครนี้

#define METHOD_MEMBER(TYPE, NAME, CLASS) \
CLASS &set_ ## NAME(const TYPE &_val) { NAME = _val; return *this; } \
TYPE NAME;

struct foo {
    METHOD_MEMBER(string, attr1, foo)
    METHOD_MEMBER(int, attr2, foo)
    METHOD_MEMBER(double, attr3, foo)
};

// inline usage
foo test = foo().set_attr1("hi").set_attr2(22).set_attr3(3.14);

มาโครนั้นสร้างแอตทริบิวต์และวิธีการอ้างอิงตนเอง


0

สำหรับเวอร์ชันของ C ++ ก่อน C ++ 20 (ซึ่งแนะนำการเริ่มต้นที่มีชื่อทำให้ตัวเลือกของคุณใช้ได้ใน C ++) ให้พิจารณาสิ่งต่อไปนี้:

int main()
{
    struct TFoo { int val; };
    struct TBar { float val; };

    struct FooBar {
        TFoo foo;
        TBar bar;
    };

    FooBar mystruct = { TFoo{12}, TBar{3.4} };

    std::cout << "foo = " << mystruct.foo.val << " bar = " << mystruct.bar.val << std::endl;

}

โปรดทราบว่าหากคุณพยายามเริ่มต้นโครงสร้างด้วย FooBar mystruct = { TFoo{12}, TFoo{3.4} };คุณจะได้รับข้อผิดพลาดในการคอมไพล์

ข้อเสียคือคุณต้องสร้างหนึ่ง struct mystruct.foo.valเพิ่มเติมสำหรับแต่ละตัวแปรภายในโครงสร้างหลักของคุณและคุณจะต้องใช้ค่าภายในด้วย แต่ในทางกลับกันมันสะอาดเรียบง่ายบริสุทธิ์และได้มาตรฐาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.