有没有办法在C中实现函数重载?我正在寻找简单的函数被重载像

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

我认为没有直接的方法;我在寻找变通办法,如果有的话。


当前回答

Yes!

自从提出这个问题以来,由于在C11中添加了_Generic关键字,标准C(没有扩展)已经有效地获得了对函数重载(不是操作符)的支持。(GCC从4.9版开始支持)

(重载并不是真正“内置”在问题中显示的方式,但它是非常容易实现这样的工作。)

_Generic是一个编译时操作符,与sizeof和_Alignof属于同一家族。在标准章节6.5.1.1中有描述。它接受两个主要参数:一个表达式(在运行时不会计算)和一个类型/表达式关联列表,它看起来有点像一个开关块。_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

在本例中,我们可以为第三种情况使用默认的:association,但这并没有演示如何将原则扩展到多个参数。最终的结果是,你可以在你的代码中使用foo(…),而不用担心它的参数类型。


对于更复杂的情况,例如函数重载更大数量的参数,或者变化的数量,你可以使用实用程序宏自动生成静态分派结构:

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

因此,通过一些努力,您可以减少样板文件的数量,使其看起来非常像具有原生重载支持的语言。

顺便说一句,在C99中已经可以重载参数的数量(而不是类型)。


注意,C计算类型的方式可能会让您出错。这将选择foo_int,如果你试图传递它一个字符字面量,例如,如果你想让你的重载支持字符串字面量,你需要一点混乱。不过总体来说还是很酷的。

其他回答

如果你的编译器支持,试着将这些函数声明为extern " c++ ", http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspx

有以下几种可能性:

Printf样式函数(类型作为参数) Opengl风格函数(输入函数名) c++的c子集(如果你能使用c++编译器)

你不能只使用c++而不使用除这个以外的所有其他c++特性吗?

如果仍然没有严格的C,那么我会推荐变进函数代替。

下面是我发现的演示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

Yes!

自从提出这个问题以来,由于在C11中添加了_Generic关键字,标准C(没有扩展)已经有效地获得了对函数重载(不是操作符)的支持。(GCC从4.9版开始支持)

(重载并不是真正“内置”在问题中显示的方式,但它是非常容易实现这样的工作。)

_Generic是一个编译时操作符,与sizeof和_Alignof属于同一家族。在标准章节6.5.1.1中有描述。它接受两个主要参数:一个表达式(在运行时不会计算)和一个类型/表达式关联列表,它看起来有点像一个开关块。_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

在本例中,我们可以为第三种情况使用默认的:association,但这并没有演示如何将原则扩展到多个参数。最终的结果是,你可以在你的代码中使用foo(…),而不用担心它的参数类型。


对于更复杂的情况,例如函数重载更大数量的参数,或者变化的数量,你可以使用实用程序宏自动生成静态分派结构:

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

因此,通过一些努力,您可以减少样板文件的数量,使其看起来非常像具有原生重载支持的语言。

顺便说一句,在C99中已经可以重载参数的数量(而不是类型)。


注意,C计算类型的方式可能会让您出错。这将选择foo_int,如果你试图传递它一个字符字面量,例如,如果你想让你的重载支持字符串字面量,你需要一点混乱。不过总体来说还是很酷的。