อะไรคือกฎสำหรับโทเค็น“ …” ในบริบทของเทมเพลตแบบต่างๆ


99

ใน C ++ 11 มีเทมเพลตที่หลากหลายเช่นนี้:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args )
{
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

มีวิทยากรบางคนเกี่ยวกับเรื่องนี้: การแสดงออกstd::forward<Args>(args)...ใช้ทั้งArgsและargsแต่เพียงหนึ่ง...โทเค็น นอกจากนี้ยังstd::forwardเป็นฟังก์ชันเทมเพลตที่ไม่แปรผันโดยใช้พารามิเตอร์เทมเพลตเดียวและอาร์กิวเมนต์เดียว กฎไวยากรณ์สำหรับสิ่งนั้น (คร่าวๆ) คืออะไร? จะสรุปได้อย่างไร?

นอกจากนี้: ในการใช้งานฟังก์ชันจุดไข่ปลา ( ...) อยู่ที่ส่วนท้ายของนิพจน์ที่สนใจ มีเหตุผลไหมที่ในรายการอาร์กิวเมนต์เทมเพลตและรายการพารามิเตอร์ที่จุดไข่ปลาอยู่ตรงกลาง


2
สรุปสั้น ๆ ในส่วนที่สอง: เมื่อ "ประกาศ" ชุดพารามิเตอร์เทมเพลตหรือชุดพารามิเตอร์ฟังก์ชัน...จะมาก่อนตัวระบุที่ถูกนำมาใช้ เมื่อใช้แพ็คอย่างใดอย่างหนึ่งหรือทั้งสองประเภท...มาหลังจากรูปแบบนิพจน์เพื่อขยาย
aschepler

คำตอบ:


99

ในบริบทของเทมเพลตแบบผันแปรจุดไข่ปลา...จะใช้เพื่อคลายแพ็กพารามิเตอร์เทมเพลตหากปรากฏขึ้นทางด้านขวาของนิพจน์ (เรียกรูปแบบนิพจน์นี้ชั่วขณะ) หรือเป็นอาร์กิวเมนต์แพ็คหากปรากฏทางด้านซ้ายของ ชื่อ:

...thing  // pack   : appears as template arguments
thing...  // unpack : appears when consuming the arguments

กฎคือว่าสิ่งที่รูปแบบที่อยู่ทางด้านซ้ายของ...ซ้ำแล้วซ้ำอีก - รูปแบบแตก (เรียกพวกเขาแสดงออกในขณะนี้) ,จะถูกคั่นด้วยเครื่องหมายจุลภาค

ตัวอย่างบางส่วนสามารถเข้าใจได้ดีที่สุด สมมติว่าคุณมีเทมเพลตฟังก์ชันนี้:

template<typename ...T> //pack
void f(T ... args)      //pack
{
   // here are unpack patterns

   g( args... );        //pattern = args
   h( x(args)... );     //pattern = x(args)
   m( y(args...) );     //pattern = args (as argument to y())
   n( z<T>(args)... );  //pattern = z<T>(args)
}

ตอนนี้ถ้าฉันเรียกฟังก์ชั่นนี้ผ่านTเป็น{int, char, short}ดังนั้นการเรียกใช้ฟังก์ชันแต่ละครั้งจะขยายเป็น:

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

ในรหัสที่คุณโพสต์ให้ทำstd::forwardตามรูปแบบที่สี่ที่แสดงโดยn()การเรียกใช้ฟังก์ชัน

สังเกตความแตกต่างระหว่างx(args)...และy(args...)สูงกว่า!


คุณสามารถใช้...เพื่อเริ่มต้นอาร์เรย์เช่น:

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

ซึ่งขยายเป็นสิ่งนี้:

std::vector<data_info> v 
{ 
   {arg0, sizeof(int)},
   {arg1, sizeof(char)},
   {arg2, sizeof(short)}
};

ฉันเพิ่งรู้ว่ารูปแบบอาจรวมถึงตัวระบุการเข้าถึงเช่นดังpublicที่แสดงในตัวอย่างต่อไปนี้:

template<typename ... Mixins>
struct mixture : public Mixins ...  //pattern = public Mixins
{
    //code
};

ในตัวอย่างนี้รูปแบบจะขยายเป็น:

struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN  

นั่นคือmixtureเกิดขึ้นอย่างเปิดเผยต่อสาธารณะจากคลาสพื้นฐานทั้งหมด

หวังว่าจะช่วยได้


1
เกี่ยวกับนิพจน์ที่ตรงกัน; ควรเป็นการแสดงออกที่ใหญ่ที่สุดเท่าที่จะเป็นไปได้หรือไม่? ยกตัวอย่างเช่นx+args...ควรจะขยายไปไม่ได้x+arg0,x+arg1,x+arg2 x+arg0,arg1,arg2
bitmask

ดังนั้นจึง...ใช้กับเอนทิตีที่ขยายได้ทุกตัวในรูปแบบ
Lightness Races ใน Orbit

@bitmask: ครับ x+args...ควรขยายไปx+arg0,x+arg1,x+arg2, ไม่ได้ x+arg0,arg1,arg2
Nawaz

1
@ Jarod42: ฉันจะอัปเดตคำตอบนี้เมื่อ C ++ 17 ออก
Nawaz

3
@synther: sizeof...(T)ไม่จำเป็นต้องมี คุณสามารถเขียน:int a[] = { ___ };
นาวาซ

48

ต่อไปนี้นำมาจากการพูดคุย"Variadic Templates are Funadic"โดย Andrei Alexandrescu ใน GoingNative 2012 ฉันสามารถแนะนำได้เพื่อเป็นการแนะนำที่ดีเกี่ยวกับเทมเพลตแบบต่างๆ


มีสองสิ่งที่หนึ่งสามารถทำได้กับแพ็คตัวแปร สามารถนำไปใช้sizeof...(vs)เพื่อรับจำนวนองค์ประกอบและขยายได้

กฎการขยายตัว

Use            Expansion

Ts...          T1, ..., Tn
Ts&&...        T1&&, ..., Tn&&
x<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

การขยายตัวดำเนินไปสู่ภายนอก เมื่อขยายสองรายการในขั้นตอนการล็อกรายการเหล่านั้นจะต้องมีขนาดเท่ากัน

ตัวอย่างเพิ่มเติม:

gun(A<Ts...>::hun(vs)...);

ขยายทั้งหมดTsในรายการแม่แบบข้อโต้แย้งของAแล้วฟังก์ชั่นได้รับการขยายตัวที่มีทั้งหมดhunvs

gun(A<Ts...>::hun(vs...));

ขยายทั้งหมดTsในรายการอาร์กิวเมนต์แม่แบบAและทุกเป็นข้อโต้แย้งฟังก์ชั่นสำหรับvshun

gun(A<Ts>::hun(vs)...);

ขยายฟังก์ชันhunด้วยTsและvsในขั้นตอนการล็อก

บันทึก:

Tsไม่ใช่ประเภทและvsไม่ใช่ค่า! เป็นนามแฝงสำหรับรายการประเภท / ค่า ทั้งสองรายการอาจว่างเปล่า ทั้งสองเชื่อฟังเฉพาะการกระทำที่เฉพาะเจาะจง ดังนั้นสิ่งต่อไปนี้เป็นไปไม่ได้:

typedef Ts MyList;  // error!
Ts var;             // error!
auto copy = vs;     // error!

ตำแหน่งการขยายตัว

อาร์กิวเมนต์ของฟังก์ชัน

template <typename... Ts>
void fun(Ts... vs)

รายการตัวเริ่มต้น

any a[] = { vs... };

ตัวระบุฐาน

template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<Ts>... { /**/ };

รายการเริ่มต้นสมาชิก

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

รายการอาร์กิวเมนต์ชั่วคราว

std::map<Ts...> m;

จะคอมไพล์ก็ต่อเมื่อมีข้อโต้แย้งที่เป็นไปได้

บันทึกรายการ

template <class... Ts> void fun(Ts... vs) {
    auto g = [&vs...] { return gun(vs...); }
    g();
}

รายการคุณสมบัติ

struct [[ Ts... ]] IAmFromTheFuture {};

มันอยู่ในข้อกำหนด แต่ยังไม่มีแอตทริบิวต์ที่สามารถแสดงเป็นประเภทได้


ดี. อาร์กิวเมนต์ของฟังก์ชันจะถูกปล่อยออกจากตำแหน่งแม้ว่า: P
Potatoswatter

@Potatoswatter ขอบคุณ ไม่ได้กล่าวถึงการขยายในรายการอาร์กิวเมนต์ของฟังก์ชัน
typ1232

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