277

Cで関数のオーバーロードを実現する方法はありますか?私は次のようにオーバーロードされる単純な関数を見ています

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

簡単な方法はないと思います。回避策があれば探しています。

4

14 に答える 14

290

はい!

この質問がされて以来、C11 にキーワードが追加されたおかげで、標準 C (拡張機能なし) は関数のオーバーロード (演算子ではない) のサポートを効果的に獲得しました。_Generic(バージョン 4.9 以降の GCC でサポートされています)

(過負荷は、質問に示されているように真に「組み込み」ではありませんが、そのように機能するものを実装するのは非常に簡単です。)

_Genericsizeofは、および と同じファミリーのコンパイル時演算子_Alignofです。これは、標準セクション 6.5.1.1 で説明されています。式 (実行時に評価されない) と、ブロックのように見える型/式の関連付けリストの 2 つの主なパラメーターを受け入れますswitch_Generic式の全体的な型を取得し、それを「切り替え」て、その型のリストで最終結果の式を選択します。

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

上記の式は次のように評価されます2- 制御式の型はintであるため、 に関連付けられた式をint値として選択します。これは実行時に何も残りません。(このdefault句はオプションです。省略した場合、型が一致しないと、コンパイル エラーが発生します。)

これが関数のオーバーロードに役立つ方法は、C プリプロセッサによって挿入され、制御マクロに渡される引数の型に基づいて結果式を選択できることです。したがって(C標準の例):

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

このマクロcbrtは、引数の型をマクロにディスパッチし、適切な実装関数を選択し、元のマクロ引数をその関数に渡すことによって、オーバーロードされた操作を実装します。

元の例を実装するには、次のようにします。

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

この場合default:、3 番目のケースに関連付けを使用することもできましたが、それは原則を複数の引数に拡張する方法を示していません。foo(...)最終的な結果として、引数の型について (あまり[1]) 気にせずにコードで使用できるようになります。


関数が多数の引数をオーバーロードしたり、数が変化したりするなど、より複雑な状況では、ユーティリティ マクロを使用して静的ディスパッチ構造を自動的に生成できます。

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

( implementation here ) したがって、ある程度の努力をすれば、定型文の量を減らして、オーバーロードをネイティブにサポートする言語のように見せることができます。

余談ですが、C99 では、(型ではなく) 引数のをオーバーロードすることが既に可能でした。


[1] ただし、C が型を評価する方法にはつまずく可能性があることに注意してください。foo_intたとえば、文字リテラルを渡そうとするかどうかを選択します。オーバーロードで文字列リテラルをサポートする場合は、少し混乱する必要があります。それでも全体的にかなりクールです。

于 2014-07-29T23:04:48.273 に答える
140

いくつかの可能性があります:

  1. printfスタイル関数(引数として入力)
  2. openglスタイルの関数(関数名を入力)
  3. c ++のサブセット(c ++コンパイラを使用できる場合)
于 2009-01-26T09:24:41.883 に答える
85

既に述べたように、あなたが意味する意味でのオーバーロードは C ではサポートされていません。問題を解決するための一般的なイディオムは、関数がタグ付きの unionを受け入れるようにすることです。これは、パラメーターによって実装されstructます。パラメーター自体は、さまざまなタイプの値のやstructなど、ある種のタイプ インジケーターで構成されます。例:enumunion

#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); 
}
于 2009-01-26T09:52:11.813 に答える
30

これは、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

于 2015-07-26T18:20:41.937 に答える
19

コンパイラがgccであり、新しいオーバーロードを追加するたびに手動更新を行うことを気にしない場合は、マクロマジックを実行して、呼び出し元に関して希望する結果を得ることができます。書くのはそれほど良いことではありません...しかし、それは可能です。

__builtin_types_compatible_pを見て、それを使用して次のようなマクロを定義します。

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

しかし、いや厄介です、ただしないでください

編集: C1Xは、次のような型ジェネリック式のサポートを取得します。

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)
于 2010-06-14T21:12:34.580 に答える
13

次のアプローチはa2800276に似ていますが、いくつかの C99 マクロ マジックが追加されています。

// 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;
}
于 2009-01-26T10:25:46.177 に答える
13

はい、そうです。

ここに例を示します:

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

printA と printB から 0 と hello .. を出力します。

于 2012-10-22T11:22:33.310 に答える
13

これはまったく役に立たないかもしれませんが、clang を使用している場合は、overloadable 属性を使用できます。これは、C としてコンパイルする場合でも機能します。

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

ヘッダ

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

実装

void __attribute__((overloadable)) DecodeImageNow(CGImageRef image, CGContextRef usingContext { ... }
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image) { ... }
于 2013-09-18T18:50:03.737 に答える
11

あなたが意味する意味で—いいえ、できません。

va_arg次のような関数を宣言できます

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

、ただし、最初の引数で変数の数とその型に関するある種の情報を渡す必要があります—printf()そうです。

于 2009-01-26T09:25:26.500 に答える
6

通常、タイプを示す疣贅は名前に追加または付加されます。マクロで逃げることができるのはいくつかの例ですが、それはむしろあなたがやろうとしていることに依存します。Cにはポリモーフィズムはなく、強制のみです。

単純な汎用操作は、マクロを使用して実行できます。

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

コンパイラがtypeofをサポートしている場合は、より複雑な操作をマクロに入れることができます。次に、シンボルfoo(x)を使用して、同じ操作を異なるタイプでサポートできますが、異なるオーバーロード間で動作を変えることはできません。マクロではなく実際の関数が必要な場合は、タイプを名前に貼り付け、2番目の貼り付けを使用してアクセスできる可能性があります(私は試していません)。

于 2009-01-26T09:30:41.827 に答える
1

C ++だけを使用して、これ以外の他のすべてのC ++機能を使用することはできませんか?

それでも厳密なCだけではない場合は、代わりに可変個引数関数をお勧めします。

于 2009-01-26T09:24:47.143 に答える
-3

extern "C++"コンパイラがこれをサポートしているかのように、これらの関数を宣言してみてください。 http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspx

于 2009-01-26T10:45:51.190 に答える
-6

以下のコードが関数のオーバーロードを理解するのに役立つことを願っています

#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 *));
}
于 2016-03-09T04:50:31.300 に答える