Làm thế nào để đạt được chức năng quá tải trong C?


240

Có cách nào để đạt được chức năng quá tải trong C không? Tôi đang xem các chức năng đơn giản để bị quá tải như

foo (int a)  
foo (char b)  
foo (float c , int d)

Tôi nghĩ rằng không có con đường thẳng về phía trước; Tôi đang tìm cách giải quyết nếu có.


6
Tại sao bạn muốn làm điều này? C không có khả năng đa hình. Vì vậy, foo (loại ngẫu nhiên) là không thể. Chỉ cần thực hiện funcs thực sự foo_i, foo_ch, foo_d, v.v.
jmucchiello

4
Bạn có thể đi theo con đường xấu bằng cách sử dụng con trỏ void và nhập id.
kiềm

11
Tôi cảm thấy mình nên chú ý đến thực tế rằng câu trả lời cho câu hỏi này đã thay đổi kể từ khi được hỏi ban đầu , với tiêu chuẩn C mới.
Leushenko

Câu trả lời:


127

Có một vài khả năng:

  1. Các hàm kiểu printf (gõ làm đối số)
  2. các hàm kiểu opengl (nhập tên hàm)
  3. tập con c của c ++ (nếu bạn có thể sử dụng trình biên dịch c ++)

1
bạn có thể giải thích hoặc cung cấp các liên kết cho các chức năng phong cách opengl?
FL4SOF

1
@Lazer: Đây là một cách thực hiện chức năng giống như printf đơn giản.
Alexey Frunze

12
Số printf không quá tải chức năng. nó sử dụng vararg !!! Và C không hỗ trợ chức năng Quá tải.
hqt

52
@hqt Câu trả lời không bao giờ đề cập đến quá tải từ.
kyrias

1
@kyrias Nếu câu trả lời không phải là quá tải thì đó là câu hỏi sai
Michael Mrozek

233

Đúng!

Trong thời gian kể từ khi câu hỏi này được hỏi, tiêu chuẩn C (không có phần mở rộng) đã đạt được hiệu quả hỗ trợ cho quá tải chức năng (không phải toán tử), nhờ vào việc thêm _Generictừ khóa trong C11. (được hỗ trợ trong GCC kể từ phiên bản 4.9)

(Quá tải không thực sự "tích hợp" theo cách được nêu trong câu hỏi, nhưng thật dễ dàng để thực hiện một cái gì đó hoạt động như vậy.)

_Genericlà một toán tử thời gian biên dịch trong cùng một gia đình sizeof_Alignof. Nó được mô tả trong phần tiêu chuẩn 6.5.1.1. Nó chấp nhận hai tham số chính: một biểu thức (sẽ không được đánh giá khi chạy) và danh sách liên kết kiểu / biểu thức trông hơi giống một switchkhối. _Genericlấy loại tổng thể của biểu thức và sau đó "chuyển đổi" trên nó để chọn biểu thức kết quả cuối cùng trong danh sách cho loại của nó:

_Generic(1, float: 2.0,
            char *: "2",
            int: 2,
            default: get_two_object());

Biểu thức trên ước tính 2- loại biểu thức kiểm soát là int, vì vậy nó chọn biểu thức được liên kết intlàm giá trị. Không có gì của điều này vẫn còn trong thời gian chạy. (Cácdefault Mệnh đề là tùy chọn: nếu bạn bỏ nó và loại không khớp, nó sẽ gây ra lỗi biên dịch.)

Cách này hữu ích cho việc nạp chồng hàm là nó có thể được chèn bởi bộ tiền xử lý C và chọn một biểu thức kết quả dựa trên loại đối số được truyền cho macro điều khiển. Vì vậy (ví dụ từ tiêu chuẩn C):

#define cbrt(X) _Generic((X),                \
                         long double: cbrtl, \
                         default: cbrt,      \
                         float: cbrtf        \
                         )(X)

Macro này thực hiện một cbrthoạt động quá tải , bằng cách gửi loại đối số tới macro, chọn một hàm thực hiện phù hợp và sau đó chuyển đối số macro ban đầu cho hàm đó.

Vì vậy, để thực hiện ví dụ ban đầu của bạn, chúng tôi có thể làm điều này:

foo_int (int a)  
foo_char (char b)  
foo_float_int (float c , int d)

#define foo(_1, ...) _Generic((_1),                                  \
                              int: foo_int,                          \
                              char: foo_char,                        \
                              float: _Generic((FIRST(__VA_ARGS__,)), \
                                     int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A

Trong trường hợp này, chúng tôi có thể đã sử dụng một default: liên kết cho trường hợp thứ ba, nhưng điều đó không thể hiện cách mở rộng nguyên tắc cho nhiều đối số. Kết quả cuối cùng là bạn có thể sử dụng foo(...)mã của mình mà không phải lo lắng (nhiều [1]) về loại đối số của nó.


Đối với các tình huống phức tạp hơn, ví dụ: các hàm quá tải số lượng đối số lớn hơn hoặc số lượng khác nhau, bạn có thể sử dụng macro tiện ích để tự động tạo cấu trúc công văn tĩnh:

void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }

#define print(...) OVERLOAD(print, (__VA_ARGS__), \
    (print_ii, (int, int)), \
    (print_di, (double, int)), \
    (print_iii, (int, int, int)) \
)

#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"

int main(void) {
    print(44, 47);   // prints "int, int"
    print(4.4, 47);  // prints "double, int"
    print(1, 2, 3);  // prints "int, int, int"
    print("");       // prints "unknown arguments"
}

( thực hiện ở đây ) Vì vậy, với một số nỗ lực, bạn có thể giảm số lượng nồi hơi xuống trông giống như một ngôn ngữ với sự hỗ trợ riêng cho quá tải.

Bên cạnh đó , nó đã có thể quá tải về số lượng đối số (không phải loại) trong C99.


[1] lưu ý rằng cách C đánh giá các loại có thể khiến bạn gặp khó khăn. Điều này sẽ chọn foo_intnếu bạn cố gắng truyền cho nó một ký tự bằng chữ, chẳng hạn, và bạn cần phải lộn xộn một chút nếu bạn muốn quá tải của mình hỗ trợ chuỗi ký tự. Nhìn chung vẫn khá tuyệt.


Dựa trên ví dụ của bạn, có vẻ như điều duy nhất bị quá tải là chức năng như macro. Hãy để tôi xem nếu tôi hiểu chính xác: Nếu bạn muốn quá tải các chức năng, bạn chỉ cần sử dụng bộ tiền xử lý để chuyển hướng cuộc gọi chức năng dựa trên các loại dữ liệu được truyền vào, phải không?
Nick

Than ôi, bất cứ khi nào C11 bắt đầu bắt kịp, tôi cho rằng MISRA sẽ không chấp nhận tính năng này vì những lý do tương tự họ cấm các danh sách đối số biến. Tôi cố gắng gắn bó bởi MISRA khá gần trong thế giới của tôi.
Nick

9
@Nick đó là tất cả quá tải là. Nó chỉ được xử lý ngầm trong các ngôn ngữ khác (ví dụ: bạn thực sự không thể có được "một con trỏ tới một hàm bị quá tải" trong bất kỳ ngôn ngữ nào, vì quá tải hàm ý nhiều cơ quan). Lưu ý rằng điều này không thể được thực hiện bởi bộ tiền xử lý một mình, nó yêu cầu một số loại; bộ tiền xử lý chỉ thay đổi giao diện của nó.
Leushenko

1
Là một người khá quen thuộc với C99 và muốn học cách làm điều này, điều này có vẻ quá phức tạp, ngay cả đối với C.
Tyler Crompton

5
@TylerCrompton Nó được đánh giá tại thời gian biên dịch.
JAB

75

Như đã nêu, quá tải theo nghĩa mà bạn muốn nói không được C. hỗ trợ Một thành ngữ phổ biến để giải quyết vấn đề là làm cho hàm chấp nhận một liên kết được gắn thẻ . Điều này được thực hiện bởi một structtham số, trong đó structchính nó bao gồm một số loại chỉ báo loại, chẳng hạn như một enumvà một uniontrong các loại giá trị khác nhau. Thí dụ:

#include <stdio.h>

typedef enum {
    T_INT,
    T_FLOAT,
    T_CHAR,
} my_type;

typedef struct {
    my_type type;
    union {
        int a; 
        float b; 
        char c;
    } my_union;
} my_struct;

void set_overload (my_struct *whatever) 
{
    switch (whatever->type) 
    {
        case T_INT:
            whatever->my_union.a = 1;
            break;
        case T_FLOAT:
            whatever->my_union.b = 2.0;
            break;
        case T_CHAR:
            whatever->my_union.c = '3';
    }
}

void printf_overload (my_struct *whatever) {
    switch (whatever->type) 
    {
        case T_INT:
            printf("%d\n", whatever->my_union.a);
            break;
        case T_FLOAT:
            printf("%f\n", whatever->my_union.b);
            break;
        case T_CHAR:
            printf("%c\n", whatever->my_union.c);
            break;
    }

}

int main (int argc, char* argv[])
{
    my_struct s;

    s.type=T_INT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_FLOAT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_CHAR;
    set_overload(&s);
    printf_overload(&s); 
}

22
Tại sao bạn sẽ không chỉ làm cho tất cả các whatevers vào chức năng riêng biệt ( set_int, set_float, vv). Sau đó "gắn thẻ với loại" trở thành "thêm tên loại vào tên hàm". Phiên bản trong câu trả lời này liên quan đến việc gõ nhiều hơn, chi phí thời gian chạy nhiều hơn, nhiều khả năng xảy ra lỗi hơn trong thời gian biên dịch ... Tôi không thấy bất kỳ lợi thế nào để làm mọi thứ theo cách này! 16 upvote?!
Ben

20
Ben, câu trả lời này được nêu lên vì nó trả lời câu hỏi, thay vì chỉ nói rằng đừng làm điều đó. Bạn đúng là sử dụng các hàm riêng biệt trong C để sử dụng các hàm riêng biệt, nhưng nếu ai đó muốn đa hình trong C, đây là một cách tốt để làm điều đó. Hơn nữa, câu trả lời này cho thấy cách bạn sẽ triển khai đa hình thời gian chạy trong trình biên dịch hoặc VM: gắn thẻ giá trị với một loại, sau đó gửi đi dựa trên đó. Đó là một câu trả lời tuyệt vời cho câu hỏi ban đầu.
Nils von Barth

20

Dưới đây là ví dụ rõ ràng và ngắn gọn nhất mà tôi đã tìm thấy chứng minh chức năng quá tải trong C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int addi(int a, int b) {
    return a + b;
}

char *adds(char *a, char *b) {
    char *res = malloc(strlen(a) + strlen(b) + 1);
    strcpy(res, a);
    strcat(res, b);
    return res;
}

#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)

int main(void) {
    int a = 1, b = 2;
    printf("%d\n", add(a, b)); // 3

    char *c = "hello ", *d = "world";
    printf("%s\n", add(c, d)); // hello world

    return 0;
}

https://gist.github.com/barosl/e0af4a92b2b8cabd05a7


1
Tôi nghĩ rằng đây là bản sao của stackoverflow.com/a/25026353/1240268 về tinh thần (nhưng với ít lời giải thích hơn).
Andy Hayden

1
Tôi chắc chắn thích 1 khối liên tục duy nhất của mã hoàn chỉnh và có thể chạy được cho phần cắt và thái hạt lựu là # 1240268. Để mỗi người của họ.
Jay Taylor

1
Tôi thích câu trả lời giải thích những gì họ đang làm và tại sao họ làm việc. Điều này cũng không. "Tốt nhất tôi từng thấy:" không phải là giải trình.
gạch dưới

19

Nếu trình biên dịch của bạn là gcc và bạn không bận tâm đến việc cập nhật tay mỗi khi bạn thêm một lần quá tải mới, bạn có thể thực hiện một số phép thuật vĩ mô và nhận được kết quả bạn muốn về mặt người gọi, nhưng nó không tốt để viết ... nhưng có thể

nhìn vào __builtin_types_compiverse_p, sau đó sử dụng nó để xác định một macro làm một cái gì đó như

#define foo(a) \
((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

nhưng khó chịu, chỉ là không

EDIT: C1X sẽ nhận được hỗ trợ cho các biểu thức chung loại trông giống như sau:

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

13

Ừ kiểu vậy, chắc vậy.

Ở đây bạn đi bằng ví dụ:

void printA(int a){
printf("Hello world from printA : %d\n",a);
}

void printB(const char *buff){
printf("Hello world from printB : %s\n",buff);
}

#define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 
#define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define _Num_ARGS_(...) __VA_ARG_N(__VA_ARGS__) 
#define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) 
#define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args)>t)
#define CHECK_ARGS_MIN_LIMIT(t) if(NUM_ARGS(args) 
#define print(x , args ...) \
CHECK_ARGS_MIN_LIMIT(1) printf("error");fflush(stdout); \
CHECK_ARGS_MAX_LIMIT(4) printf("error");fflush(stdout); \
({ \
if (__builtin_types_compatible_p (typeof (x), int)) \
printA(x, ##args); \
else \
printB (x,##args); \
})

int main(int argc, char** argv) {
    int a=0;
    print(a);
    print("hello");
    return (EXIT_SUCCESS);
}

Nó sẽ xuất 0 và xin chào .. từ printA và printB.


2
int main (int argc, char ** argv) {int a = 0; in (a); in ("xin chào"); trả lại (EXIT_SUCCESS); } sẽ xuất 0 và xin chào .. từ printA và printB ...
Captain Barbossa

1
__builtin_types_compiverse_p, trình biên dịch GCC đó có cụ thể không?
Sogartar

11

Cách tiếp cận sau đây tương tự như a2800276 , nhưng với một số phép thuật vĩ mô C99 được thêm vào:

// we need `size_t`
#include <stddef.h>

// argument types to accept
enum sum_arg_types { SUM_LONG, SUM_ULONG, SUM_DOUBLE };

// a structure to hold an argument
struct sum_arg
{
    enum sum_arg_types type;
    union
    {
        long as_long;
        unsigned long as_ulong;
        double as_double;
    } value;
};

// determine an array's size
#define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY)))

// this is how our function will be called
#define sum(...) _sum(count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__))

// create an array of `struct sum_arg`
#define sum_args(...) ((struct sum_arg []){ __VA_ARGS__ })

// create initializers for the arguments
#define sum_long(VALUE) { SUM_LONG, { .as_long = (VALUE) } }
#define sum_ulong(VALUE) { SUM_ULONG, { .as_ulong = (VALUE) } }
#define sum_double(VALUE) { SUM_DOUBLE, { .as_double = (VALUE) } }

// our polymorphic function
long double _sum(size_t count, struct sum_arg * args)
{
    long double value = 0;

    for(size_t i = 0; i < count; ++i)
    {
        switch(args[i].type)
        {
            case SUM_LONG:
            value += args[i].value.as_long;
            break;

            case SUM_ULONG:
            value += args[i].value.as_ulong;
            break;

            case SUM_DOUBLE:
            value += args[i].value.as_double;
            break;
        }
    }

    return value;
}

// let's see if it works

#include <stdio.h>

int main()
{
    unsigned long foo = -1;
    long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10));
    printf("%Le\n", value);
    return 0;
}

11

Điều này có thể không giúp ích gì cả, nhưng nếu bạn đang sử dụng tiếng kêu, bạn có thể sử dụng thuộc tính quá tải - Điều này hoạt động ngay cả khi biên dịch thành C

http://clang.llvm.org/docs/AttributionReference.html#overloadable

Tiêu đề

extern void DecodeImageNow(CGImageRef image, CGContextRef usingContext) __attribute__((overloadable));
extern void DecodeImageNow(CGImageRef image) __attribute__((overloadable));

Thực hiện

void __attribute__((overloadable)) DecodeImageNow(CGImageRef image, CGContextRef usingContext { ... }
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image) { ... }

10

Theo nghĩa của bạn - không, bạn không thể.

Bạn có thể khai báo một va_arghàm như

void my_func(char* format, ...);

, nhưng bạn sẽ cần truyền một số loại thông tin về số lượng biến và loại của chúng trong đối số đầu tiên - giống như printf()vậy.


6

Thông thường một mụn cóc để chỉ ra loại được gắn thêm hoặc thêm vào tên. Bạn có thể thoát khỏi macro là một số trường hợp, nhưng nó phụ thuộc vào những gì bạn đang cố gắng làm. Không có tính đa hình trong C, chỉ có sự ép buộc.

Các thao tác chung đơn giản có thể được thực hiện với các macro:

#define max(x,y) ((x)>(y)?(x):(y))

Nếu trình biên dịch của bạn hỗ trợ typeof , các hoạt động phức tạp hơn có thể được đưa vào macro. Sau đó, bạn có thể có ký hiệu foo (x) để hỗ trợ cùng loại hoạt động khác nhau, nhưng bạn không thể thay đổi hành vi giữa các tình trạng quá tải khác nhau. Nếu bạn muốn các chức năng thực tế thay vì macro, bạn có thể dán loại này vào tên và sử dụng lần dán thứ hai để truy cập vào nó (Tôi chưa thử).


bạn có thể giải thích thêm một chút về cách tiếp cận dựa trên vĩ mô.
FL4SOF

4

Câu trả lời của Leushenko thực sự rất tuyệt - chỉ: fooví dụ không biên dịch với GCC, mà không thành công foo(7), vấp phải FIRSTmacro và cuộc gọi chức năng thực tế ( (_1, __VA_ARGS__), còn lại với dấu phẩy dư thừa. Ngoài ra, chúng tôi gặp rắc rối nếu chúng tôi muốn cung cấp thêm quá tải , nhu lafoo(double) .

Vì vậy, tôi quyết định xây dựng câu trả lời xa hơn một chút, bao gồm cho phép quá tải khoảng trống (foo(void) - gây ra khá nhiều rắc rối ...).

Ý tưởng bây giờ là: Xác định nhiều hơn một chung trong các macro khác nhau và cho phép chọn đúng theo số lượng đối số!

Số lượng đối số khá dễ dàng, dựa trên câu trả lời này :

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

Điều đó thật tuyệt, chúng tôi giải quyết một trong hai SELECT_1hoặc SELECT_2(hoặc nhiều đối số hơn, nếu bạn muốn / cần chúng), vì vậy chúng tôi chỉ cần định nghĩa phù hợp:

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1),    \
        int: foo_int,                   \
        char: foo_char,                 \
        double: foo_double              \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

OK, tôi đã thêm quá tải khoảng trống rồi - tuy nhiên, cái này thực sự không được bao phủ bởi tiêu chuẩn C, không cho phép các đối số dao động trống, tức là chúng tôi dựa vào các phần mở rộng của trình biên dịch !

Đầu tiên, một cuộc gọi macro trống ( foo()) vẫn tạo ra một mã thông báo, nhưng một cuộc gọi trống. Vì vậy, macro đếm thực sự trả về 1 thay vì 0 ngay cả trong lệnh gọi macro trống. Chúng tôi có thể "dễ dàng" loại bỏ vấn đề này, nếu chúng tôi đặt dấu phẩy sau __VA_ARGS__ điều kiện , tùy thuộc vào danh sách có trống hay không:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

Điều đó có vẻ dễ dàng, nhưng COMMAmacro là khá nặng nề; may mắn thay, chủ đề đã được đề cập trong một blog của Jens Gustyt (cảm ơn, Jens). Thủ thuật cơ bản là các macro chức năng không được mở rộng nếu không được theo dấu ngoặc đơn, để giải thích thêm, hãy xem blog của Jens ... Chúng ta chỉ cần sửa đổi các macro một chút theo nhu cầu của mình (Tôi sẽ sử dụng tên ngắn hơn và ít tranh luận cho sự ngắn gọn).

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,

Và bây giờ chúng tôi ổn ...

Mã hoàn chỉnh trong một khối:

/*
 * demo.c
 *
 *  Created on: 2017-09-14
 *      Author: sboehler
 */

#include <stdio.h>

void foo_void(void)
{
    puts("void");
}
void foo_int(int c)
{
    printf("int: %d\n", c);
}
void foo_char(char c)
{
    printf("char: %c\n", c);
}
void foo_double(double c)
{
    printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
    printf("double: %.2f, int: %d\n", c, d);
}

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
        int: foo_int,                \
        char: foo_char,              \
        double: foo_double           \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N

#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,

int main(int argc, char** argv)
{
    foo();
    foo(7);
    foo(10.12);
    foo(12.10, 7);
    foo((char)'s');

    return 0;
}

1

Bạn có thể chỉ sử dụng C ++ và không sử dụng tất cả các tính năng C ++ khác ngoại trừ tính năng này không?

Nếu vẫn không chỉ C nghiêm ngặt thì tôi sẽ đề nghị các hàm matrixdic thay thế.


3
Không phải nếu trình biên dịch C ++ không có sẵn cho HĐH mà anh ta đang mã hóa.
Brian

2
không chỉ vậy mà anh ta có thể muốn có một ABI C không có tên xáo trộn trong đó.
Spudd86


-4

Tôi hy vọng đoạn mã dưới đây sẽ giúp bạn hiểu quá tải chức năng

#include <stdio.h>
#include<stdarg.h>

int fun(int a, ...);
int main(int argc, char *argv[]){
   fun(1,10);
   fun(2,"cquestionbank");
   return 0;
}
int fun(int a, ...){
  va_list vl;
  va_start(vl,a);

  if(a==1)
      printf("%d",va_arg(vl,int));
   else
      printf("\n%s",va_arg(vl,char *));
}

2
Một câu trả lời sẽ giải thích những gì nó đang làm và tại sao nó hoạt động. Nếu nó không, làm thế nào nó có thể giúp bất cứ ai hiểu bất cứ điều gì?
gạch dưới

Không có quá tải ở đây.
melpomene

va_end không bao giờ được gọi
user2262111
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.