จำนวนตัวแปรที่ขัดแย้งใน C ++?


293

ฉันจะเขียนฟังก์ชันที่ยอมรับอาร์กิวเมนต์จำนวนตัวแปรได้อย่างไร เป็นไปได้อย่างไร


49
ในเวลานี้ด้วยC ++ 11คำตอบสำหรับคำถามนี้จะแตกต่างกันอย่างมาก
K-ballo

1
@ K-ballo ฉันได้เพิ่ม C ++ 11 ตัวอย่างเช่นกันเนื่องจากคำถามล่าสุดถามสิ่งเดียวกันเมื่อเร็ว ๆ นี้และฉันรู้สึกว่าสิ่งนี้จำเป็นเพื่อยืนยันการปิดstackoverflow.com/questions/16337459/…
Shafik Yaghmour

1
เพิ่มตัวเลือกก่อนC ++ 11ลงในคำตอบของฉันด้วยดังนั้นจึงควรครอบคลุมตัวเลือกส่วนใหญ่ที่มีอยู่
Shafik Yaghmour

@ K-ballo มี afaik ไม่มีวิธีที่จะทำใน C ++ ในกรณีที่คุณต้องการบังคับชนิดอาร์กิวเมนต์ .. ไม่มีสิ่งปลูกสร้างเช่น foo (ค่า int ... ค่า): / ถ้าคุณไม่สนใจเกี่ยวกับประเภทแล้วใช่แม่แบบแปรปรวน ใน C ++ 11 ใช้งานได้ดี
graywolf

คำตอบ:


152

คุณอาจไม่ควรและคุณสามารถทำสิ่งที่คุณต้องการได้อย่างปลอดภัยและง่ายขึ้น เทคนิคการใช้จำนวนตัวแปรของข้อโต้แย้งใน C คุณรวม stdarg.h จากที่คุณจะได้รับva_listประเภทเช่นเดียวกับสามฟังก์ชั่นที่ทำงานบนที่เรียกว่าva_start(), และva_arg()va_end()

#include<stdarg.h>

int maxof(int n_args, ...)
{
    va_list ap;
    va_start(ap, n_args);
    int max = va_arg(ap, int);
    for(int i = 2; i <= n_args; i++) {
        int a = va_arg(ap, int);
        if(a > max) max = a;
    }
    va_end(ap);
    return max;
}

หากคุณถามฉันว่านี่เป็นระเบียบ มันดูไม่ดีไม่ปลอดภัยและเต็มไปด้วยรายละเอียดทางเทคนิคที่ไม่เกี่ยวข้องกับสิ่งที่คุณกำลังพยายามทำสำเร็จ ให้ลองใช้ overloading หรือการสืบทอด / polymorphism, builder pattern (เช่นในoperator<<()streams) หรือ default arguments เป็นต้นซึ่งปลอดภัยกว่านี้: คอมไพเลอร์รู้มากขึ้นเกี่ยวกับสิ่งที่คุณพยายามทำมีหลายครั้งที่มันสามารถหยุดได้ คุณก่อนที่คุณจะโบยขาของคุณออก


7
สันนิษฐานว่าคุณไม่สามารถส่งผ่านการอ้างอิงไปยังฟังก์ชัน varargs ได้เนื่องจากคอมไพเลอร์ไม่ทราบว่าจะส่งผ่านค่าเมื่อใดและโดยการอ้างอิงและเนื่องจากมาโคร C พื้นฐานไม่จำเป็นต้องรู้ว่าต้องทำอะไรกับการอ้างอิง - มีข้อ จำกัด อยู่แล้ว คุณสามารถส่งผ่านไปยังฟังก์ชัน C ที่มีอาร์กิวเมนต์ที่ต่างกันได้เนื่องจากสิ่งต่างๆเช่นกฎการเลื่อนระดับ
Jonathan Leffler

3
จำเป็นต้องระบุอาร์กิวเมนต์อย่างน้อยหนึ่งอาร์กิวเมนต์ก่อน...ไวยากรณ์หรือไม่
Lazer

3
@Lazer ไม่ใช่ภาษาหรือข้อกำหนดของห้องสมุด แต่ไลบรารี่มาตรฐานไม่ได้ให้ความหมายที่จะบอกความยาวของรายการ คุณต้องมีผู้โทรเพื่อให้ข้อมูลนี้กับคุณ printf()ตัวอย่างเช่นในกรณีของฟังก์ชั่นแยกวิเคราะห์อาร์กิวเมนต์สตริงสำหรับโทเค็นพิเศษที่จะคิดออกจำนวนอาร์กิวเมนต์พิเศษที่ควรคาดหวังในรายการอาร์กิวเมนต์ตัวแปร
wilhelmtell

11
คุณควรใช้<cstdarg>ใน C ++ แทน<stdarg.h>
newacct

11
จำนวนตัวแปรที่เหมาะสมสำหรับการดีบั๊กหรือฟังก์ชั่น / วิธีการที่เติมอาร์เรย์ นอกจากนี้ยังยอดเยี่ยมสำหรับการดำเนินการทางคณิตศาสตร์หลายอย่างเช่นสูงสุด, นาที, ผลรวม, เฉลี่ย ... ไม่ยุ่งเมื่อคุณไม่ยุ่งกับมัน
Tomáš Zato - Reinstate Monica

395

ในC ++ 11คุณมีสองตัวเลือกใหม่เนื่องจากหน้าการอ้างอิงฟังก์ชัน Variadicในสถานะส่วนทางเลือก :

  • เทมเพลต Variadic ยังสามารถใช้เพื่อสร้างฟังก์ชั่นที่รับจำนวนตัวแปรที่มีข้อโต้แย้ง พวกเขามักจะเป็นทางเลือกที่ดีกว่าเพราะพวกเขาไม่ได้กำหนดข้อ จำกัด เกี่ยวกับประเภทของการขัดแย้งไม่ได้ดำเนินการส่งเสริมการขายที่สมบูรณ์และจุดลอยตัวและปลอดภัย (ตั้งแต่ C ++ 11)
  • หากอาร์กิวเมนต์ทั้งหมดแบ่งใช้ชนิดทั่วไป std :: initializer_list จะจัดเตรียมกลไกอำนวยความสะดวก (แม้ว่าจะมีไวยากรณ์แตกต่างกัน) สำหรับการเข้าถึงอาร์กิวเมนต์ของตัวแปร

ด้านล่างเป็นตัวอย่างที่แสดงทั้งทางเลือก ( ดูสด ):

#include <iostream>
#include <string>
#include <initializer_list>

template <typename T>
void func(T t) 
{
    std::cout << t << std::endl ;
}

template<typename T, typename... Args>
void func(T t, Args... args) // recursive variadic function
{
    std::cout << t <<std::endl ;

    func(args...) ;
}

template <class T>
void func2( std::initializer_list<T> list )
{
    for( auto elem : list )
    {
        std::cout << elem << std::endl ;
    }
}

int main()
{
    std::string
        str1( "Hello" ),
        str2( "world" );

    func(1,2.5,'a',str1);

    func2( {10, 20, 30, 40 }) ;
    func2( {str1, str2 } ) ;
} 

หากคุณกำลังใช้gccหรือclangเราสามารถใช้ตัวแปรเวทPRETTY_FUNCTION เพื่อแสดงลายเซ็นประเภทของฟังก์ชั่นซึ่งจะมีประโยชน์ในการทำความเข้าใจสิ่งที่เกิดขึ้น ตัวอย่างเช่นการใช้:

std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;

จะให้ผลลัพธ์ที่ตามมาสำหรับฟังก์ชัน Variadic ในตัวอย่าง ( ดูแบบสด ):

void func(T, Args...) [T = int, Args = <double, char, std::basic_string<char>>]: 1
void func(T, Args...) [T = double, Args = <char, std::basic_string<char>>]: 2.5
void func(T, Args...) [T = char, Args = <std::basic_string<char>>]: a
void func(T) [T = std::basic_string<char>]: Hello

ใน Visual Studio คุณสามารถใช้FUNCSIG

อัปเดตพื้นฐาน C ++ 11

Pre C ++ 11ทางเลือกสำหรับstd :: initializer_listน่าจะเป็นstd :: vectorหรือหนึ่งในคอนเทนเนอร์มาตรฐานอื่น:

#include <iostream>
#include <string>
#include <vector>

template <class T>
void func1( std::vector<T> vec )
{
    for( typename std::vector<T>::iterator iter = vec.begin();  iter != vec.end(); ++iter )
    {
        std::cout << *iter << std::endl ;
    }
}

int main()
{
    int arr1[] = {10, 20, 30, 40} ;
    std::string arr2[] = { "hello", "world" } ; 
    std::vector<int> v1( arr1, arr1+4 ) ;
    std::vector<std::string> v2( arr2, arr2+2 ) ;

    func1( v1 ) ;
    func1( v2 ) ;
}

และทางเลือกสำหรับแม่ variadicจะเป็นฟังก์ชั่น variadicแม้ว่าพวกเขาจะไม่ได้พิมพ์ปลอดภัยและโดยทั่วไปข้อผิดพลาดง่ายและสามารถที่จะปลอดภัยในการใช้แต่ทางเลือกที่มีศักยภาพอื่น ๆ เพียง แต่จะใช้การขัดแย้งเริ่มต้นแต่ที่มีการใช้งาน จำกัด ตัวอย่างด้านล่างเป็นตัวอย่างโค้ดที่แก้ไขแล้วในการอ้างอิงที่เชื่อมโยง:

#include <iostream>
#include <string>
#include <cstdarg>

void simple_printf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    while (*fmt != '\0') {
        if (*fmt == 'd') {
            int i = va_arg(args, int);
            std::cout << i << '\n';
        } else if (*fmt == 's') {
            char * s = va_arg(args, char*);
            std::cout << s << '\n';
        }
        ++fmt;
    }

    va_end(args);
}


int main()
{
    std::string
        str1( "Hello" ),
        str2( "world" );

    simple_printf("dddd", 10, 20, 30, 40 );
    simple_printf("ss", str1.c_str(), str2.c_str() ); 

    return 0 ;
} 

การใช้ฟังก์ชัน Variadicยังมาพร้อมกับข้อ จำกัด ในอาร์กิวเมนต์ที่คุณสามารถส่งผ่านซึ่งมีรายละเอียดในมาตรฐาน C ++ ฉบับร่างในส่วนการ5.2.2 เรียกใช้ฟังก์ชันย่อหน้า7 :

เมื่อไม่มีพารามิเตอร์สำหรับอาร์กิวเมนต์ที่กำหนดอาร์กิวเมนต์จะถูกส่งผ่านในลักษณะที่ฟังก์ชันรับสามารถรับค่าของอาร์กิวเมนต์โดยเรียกใช้ va_arg (18.7) การแปลง lvalue-to-rvalue (4.1), array-to-pointer (4.2) และ function-to-pointer (4.3) การแปลงมาตรฐานจะดำเนินการในการแสดงออกของอาร์กิวเมนต์ หลังจากการแปลงเหล่านี้หากอาร์กิวเมนต์ไม่มีเลขคณิตการนับตัวชี้ตัวชี้ไปยังสมาชิกหรือประเภทคลาสโปรแกรมจะเกิดรูปแบบไม่ถูกต้อง หากอาร์กิวเมนต์มีประเภทคลาสที่ไม่ใช่ POD (ข้อ 9) พฤติกรรมจะไม่ได้กำหนด [ ... ]


การใช้งานtypenamevs คุณclassเหนือความตั้งใจหรือไม่? ถ้าเป็นเช่นนั้นโปรดอธิบาย
kevinarpe

1
@kevinarpe ไม่ได้ตั้งใจก็ไม่ได้เปลี่ยนอะไรเลย
Shafik Yaghmour

ลิงก์แรกของคุณน่าจะเป็นไปได้ว่าอาจจะเป็นen.cppreference.com/w/cpp/language/variadic_argumentsแทน
Alexey Romanov

เป็นไปได้หรือไม่ที่จะทำให้ฟังก์ชั่นรับฟังinitializer_listซ้ำ?
idclev 463035818

33

โซลูชัน C ++ 17: ความปลอดภัยเต็มรูปแบบ + ไวยากรณ์การโทรที่ดี

เนื่องจากการเปิดตัวของเทมเพลต variadic ใน C ++ 11 และการแสดงออกเท่าใน C ++ 17 จึงเป็นไปได้ที่จะกำหนดเทมเพลตฟังก์ชั่นที่ไซต์ผู้โทรสามารถเรียกได้ว่าเป็นฟังก์ชั่น varidic แต่มีข้อดี :

  • ขอพิมพ์ให้ปลอดภัย
  • ทำงานโดยไม่มีข้อมูลรันไทม์ของจำนวนอาร์กิวเมนต์หรือไม่มีการใช้อาร์กิวเมนต์ "หยุด"

นี่คือตัวอย่างสำหรับประเภทอาร์กิวเมนต์แบบผสม

template<class... Args>
void print(Args... args)
{
    (std::cout << ... << args) << "\n";
}
print(1, ':', " Hello", ',', " ", "World!");

และอีกประเภทหนึ่งที่มีการจับคู่ประเภทที่บังคับใช้สำหรับอาร์กิวเมนต์ทั้งหมด:

#include <type_traits> // enable_if, conjuction

template<class Head, class... Tail>
using are_same = std::conjunction<std::is_same<Head, Tail>...>;

template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
void print_same_type(Head head, Tail... tail)
{
    std::cout << head;
    (std::cout << ... << tail) << "\n";
}
print_same_type("2: ", "Hello, ", "World!");   // OK
print_same_type(3, ": ", "Hello, ", "World!"); // no matching function for call to 'print_same_type(int, const char [3], const char [8], const char [7])'
                                               // print_same_type(3, ": ", "Hello, ", "World!");
                                                                                              ^

ข้อมูลมากกว่านี้:

  1. แม่ variadic ยังเป็นที่รู้จักแพ็คพารามิเตอร์พารามิเตอร์แพ็ค (ตั้งแต่ C ++ 11) - cppreference.com
  2. พับแสดงออกพับแสดงออก (ตั้งแต่ C ++ 17) - cppreference.com
  3. ดูการสาธิตแบบเต็มโปรแกรมใน coliru

13
หวังว่าวันหนึ่งฉันจะอ่านtemplate<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
Eladian

1
@Eladian อ่านว่า "สิ่งนี้จะเปิดใช้เฉพาะในกรณีที่HeadและTail... เหมือนกัน " ซึ่ง " จะเหมือนกัน " std::conjunction<std::is_same<Head, Tail>...>หมายถึง อ่านคำจำกัดความสุดท้ายนี้ว่า " Headเหมือนกันกับทั้งหมดTail..."
YSC

24

ใน c ++ 11 คุณสามารถทำได้:

void foo(const std::list<std::string> & myArguments) {
   //do whatever you want, with all the convenience of lists
}

foo({"arg1","arg2"});

รายการเริ่มต้น FTW!


19

ใน C ++ 11 มีวิธีการทำเทมเพลตอาร์กิวเมนต์ของตัวแปรซึ่งนำไปสู่วิธีที่ปลอดภัยและพิมพ์ด้วยวิธีที่ปลอดภัยเพื่อให้มีฟังก์ชันอาร์กิวเมนต์ของตัวแปร Bjarne ตัวเองให้เป็นตัวอย่างที่ดีของprintf โดยใช้แม่แบบอาร์กิวเมนต์ตัวแปรในภาษา C ++ 11FAQ

โดยส่วนตัวแล้วฉันคิดว่าสิ่งนี้สวยงามมากจนฉันจะไม่รำคาญกับฟังก์ชั่นอาร์กิวเมนต์ตัวแปรใน C ++ จนกว่าคอมไพเลอร์นั้นจะรองรับเทมเพลตอาร์กิวเมนต์ C ++ 11


@donlan - ถ้าคุณใช้ C ++ 17 คุณสามารถใช้นิพจน์แบบพับได้เพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้นมากในบางกรณี (คิดว่าสร้างสรรค์ที่นี่คุณสามารถใช้,โอเปอเรเตอร์กับนิพจน์แบบพับได้) ไม่อย่างนั้นฉันก็ไม่คิดอย่างนั้น
Omnifarious

15

รองรับฟังก์ชั่น Variadic แบบ C ใน C ++

อย่างไรก็ตามไลบรารี C ++ ส่วนใหญ่ใช้สำนวนทางเลือกเช่นในขณะที่'c' printfฟังก์ชั่นรับอาร์กิวเมนต์ที่มีตัวแปรc++ coutวัตถุจะใช้การ<<โหลดมากเกินไปซึ่งระบุประเภทความปลอดภัยและ ADT


นอกจากนี้ดูเหมือนว่าจะใช้งานได้เฉพาะในกรณีของฟังก์ชั่นเช่นการพิมพ์เท่านั้นซึ่งจริงๆแล้วคุณมีการวนซ้ำของฟังก์ชันอาร์กิวเมนต์เดี่ยวในแต่ละอาร์กิวเมนต์ มิฉะนั้นคุณเพียงแค่เริ่มต้นรายการและส่งผ่านรายการสำหรับจุดสิ้นสุดในstd::initializer_lists... และนี่คือการแนะนำความซับซ้อนขนาดใหญ่ในงานง่าย ๆ
คริสโตเฟอร์

13

นอกเหนือจาก varargs หรือการบรรทุกเกินพิกัดคุณสามารถพิจารณารวมอาร์กิวเมนต์ของคุณใน std :: vector หรือคอนเทนเนอร์อื่น ๆ (เช่น std :: map เป็นต้น) บางสิ่งเช่นนี้

template <typename T> void f(std::vector<T> const&);
std::vector<int> my_args;
my_args.push_back(1);
my_args.push_back(2);
f(my_args);

ด้วยวิธีนี้คุณจะได้รับความปลอดภัยประเภทและความหมายเชิงตรรกะของข้อโต้แย้งแปรปรวนเหล่านี้จะปรากฏขึ้น

แน่นอนว่าวิธีการนี้อาจมีปัญหาด้านประสิทธิภาพ แต่คุณไม่ควรกังวลเกี่ยวกับปัญหาเหล่านั้นเว้นแต่คุณจะแน่ใจว่าคุณไม่สามารถชำระราคา มันเป็นวิธีการแบบ "Pythonic" เพื่อ c ++ ...


6
ตัวทำความสะอาดจะไม่บังคับใช้พาหะ ใช้อาร์กิวเมนต์เท็มเพลตที่ระบุสไตล์คอลเลกชัน STL แทนแล้ววนซ้ำมันโดยใช้วิธีการเริ่มต้นและสิ้นสุดของอาร์กิวเมนต์ วิธีนี้คุณสามารถใช้ std :: vector <T>, std :: array <T, N>, c ++ 11, std :: initializer_list <T> หรือแม้แต่สร้างคอลเล็กชันของคุณเอง
Jens Åkerblom

3
@ JensÅkerblomฉันเห็นด้วย แต่นี่เป็นทางเลือกที่ควรได้รับการวิเคราะห์สำหรับปัญหาที่จะเกิดขึ้นเพื่อหลีกเลี่ยงทางวิศวกรรม เนื่องจากนี่เป็นเรื่องของลายเซ็น API จึงเป็นสิ่งสำคัญที่จะต้องเข้าใจการแลกเปลี่ยนระหว่างความยืดหยุ่นสูงสุดและความชัดเจนของเจตนา / การใช้งาน / การบำรุงรักษา ฯลฯ
Francesco

8

วิธีเดียวที่จะผ่านการใช้ข้อโต้แย้งตัวแปรสไตล์ C ตามที่อธิบายไว้ที่นี่ โปรดทราบว่านี่ไม่ใช่วิธีปฏิบัติที่แนะนำเนื่องจากไม่ใช่รูปแบบที่ปลอดภัยและผิดพลาด


โดยข้อผิดพลาดได้ง่ายฉันคิดว่าคุณหมายถึงอาจเป็นอันตรายมาก? โดยเฉพาะอย่างยิ่งเมื่อทำงานกับอินพุตที่ไม่น่าเชื่อถือ
เควิน Loney

ใช่ แต่เป็นเพราะปัญหาความปลอดภัยของประเภท คิดว่าปัญหาที่เป็นไปได้ทั้งหมดที่ printf ปกติมีอยู่: สตริงรูปแบบที่ไม่ตรงกับอาร์กิวเมนต์ที่ส่งผ่านและเช่นนั้น printf ใช้เทคนิคเดียวกันคือ BTW
Dave Van den Eynde

7

ไม่มีวิธี C ++ มาตรฐานในการทำเช่นนี้โดยไม่ต้องหันไปใช้ varargs สไตล์ C ( ...)

มีอาร์กิวเมนต์เริ่มต้นแน่นอนที่เรียงลำดับของ "ดู" เช่นหมายเลขตัวแปรของอาร์กิวเมนต์ขึ้นอยู่กับบริบท:

void myfunc( int i = 0, int j = 1, int k = 2 );

// other code...

myfunc();
myfunc( 2 );
myfunc( 2, 1 );
myfunc( 2, 1, 0 );

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


4

เป็นไปได้ที่คุณต้องการโหลดมากเกินไปหรือพารามิเตอร์เริ่มต้น - กำหนดฟังก์ชั่นเดียวกันกับพารามิเตอร์ที่ผิดนัด:

void doStuff( int a, double termstator = 1.0, bool useFlag = true )
{
   // stuff
}

void doStuff( double std_termstator )
{
   // assume the user always wants '1' for the a param
   return doStuff( 1, std_termstator );
}

วิธีนี้จะช่วยให้คุณสามารถเรียกวิธีด้วยหนึ่งในสี่สายที่แตกต่างกัน:

doStuff( 1 );
doStuff( 2, 2.5 );
doStuff( 1, 1.0, false );
doStuff( 6.72 );

... หรือคุณอาจกำลังมองหา v_args เรียกการประชุมจากซี


2

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

f(int a)
    {int res=a; return res;}
f(int a, int b)
    {int res=a+b; return res;}

และอื่น ๆ ...


2

การใช้เท็มเพลต Variadic ตัวอย่างเพื่อสร้างซ้ำconsole.logตามที่เห็นใน JavaScript:

Console console;
console.log("bunch", "of", "arguments");
console.warn("or some numbers:", 1, 2, 3);
console.error("just a prank", "bro");

ชื่อไฟล์เช่นjs_console.h:

#include <iostream>
#include <utility>

class Console {
protected:
    template <typename T>
    void log_argument(T t) {
        std::cout << t << " ";
    }
public:
    template <typename... Args>
    void log(Args&&... args) {
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }

    template <typename... Args>
    void warn(Args&&... args) {
        cout << "WARNING: ";
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }

    template <typename... Args>
    void error(Args&&... args) {
        cout << "ERROR: ";
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }
};

1

ดังที่คนอื่น ๆ บอกไว้ว่า var-style แบบ C แต่คุณสามารถทำสิ่งที่คล้ายกับอาร์กิวเมนต์เริ่มต้นได้


คุณสามารถทำอย่างละเอียด?
คดีกองทุนของโมนิกา

@QPaysTaxes: สำหรับตัวอย่างของการใช้อาร์กิวเมนต์เริ่มต้นให้ดูที่คำตอบของ Zoltan
โทมัส Padron-McCarthy

0

เป็นไปได้แล้วในตอนนี้ ... โดยใช้การเพิ่มใด ๆ และแม่แบบในกรณีนี้ประเภทอาร์กิวเมนต์สามารถผสมกันได้

#include <boost/any.hpp>
#include <iostream>

#include <vector>
using boost::any_cast;

template <typename T, typename... Types> 
void Alert(T var1,Types... var2) 
{ 

    std::vector<boost::any> a(  {var1,var2...});

    for (int i = 0; i < a.size();i++)
    {

    if (a[i].type() == typeid(int))
    {
        std::cout << "int "  << boost::any_cast<int> (a[i]) << std::endl;
    }
    if (a[i].type() == typeid(double))
    {
        std::cout << "double "  << boost::any_cast<double> (a[i]) << std::endl;
    }
    if (a[i].type() == typeid(const char*))
    {
        std::cout << "char* " << boost::any_cast<const char*> (a[i]) <<std::endl;
    }
    // etc
    }

} 


void main()
{
    Alert("something",0,0,0.3);
}

0

รวมโซลูชัน C และ C ++ สำหรับตัวเลือกที่ง่ายที่สุด semantically, performant และไดนามิกมากที่สุด หากคุณพลาดลองสิ่งอื่น

// spawn: allocate and initialize (a simple function)
template<typename T>
T * spawn(size_t n, ...){
  T * arr = new T[n];
  va_list ap;
  va_start(ap, n);
  for (size_t i = 0; i < n; i++)
    T[i] = va_arg(ap,T);
  return arr;
}

ผู้ใช้เขียน:

auto arr = spawn<float> (3, 0.1,0.2,0.3);

ความหมายนี่จะมีลักษณะและให้ความรู้สึกเหมือนกับฟังก์ชัน n-argument ภายใต้ประทุนคุณอาจนำออกมาทางเดียวหรืออีกอันหนึ่ง


-1

นอกจากนี้เรายังสามารถใช้ initializer_list หากอาร์กิวเมนต์ทั้งหมดเป็น const และเป็นประเภทเดียวกัน


-2
int fun(int n_args, ...) {
   int *p = &n_args; 
   int s = sizeof(int);
   p += s + s - 1;
   for(int i = 0; i < n_args; i++) {
     printf("A1 %d!\n", *p);
     p += 2;
   }
}

รุ่นธรรมดา


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