I think when the function being forward declared is defined in a different language than calling one, extern would be required.
I should warn you that your question is poorly phrased. I'm pretty sure you're interested in when to use
extern "C" int foo(int);
in C++, but I'm not sure, and I have to hope I'm not wasting my time.
Let's distinguish between compiler and linker. I'm leaving out some details, but nothing that affects the answer to your question.
A forward declaration is used by the compiler. In declaring the function, you give the compiler information about how it is used. You declare function F, and when the compiler runs across F being used, it knows what to do. (In the K&R days, in the absence of a declaration the compiler used defaults, sometimes leading to hilarious results. So now they're mandatory in both C and C++.)
Usually a forward declaration of a function is a functional prototype: it provides the parameter types. In C, that's not strictly necessary. You may write
int foo();
which tells the compiler foo
is a function returning int
, but not what parameters it takes. It then becomes your responsibility to get those right, because the compiler cannot check.
An extern
declaration -- whether of a function or a variable -- notifies the compiler of a symbol and its type, and says the definition will be provided by another module. The compiler leaves a placeholder for the linker to fill later.
If you look at the getopt(3) man page, for example, you'll see optarg
is declared extern
; it's defined in the C runtime library, but you can use it because it's been declared.
Now we come to the linker, and the extern "C"
of C++.
To the linker, a module exposes symbols. Global variables and functions not declared static
have external linkage, meaning they can be used by other modules.
In C, there's a 1:1 correspondence between function names and external symbols. The name is the symbol. For example, getopt(3) has a symbol of the same name in the C standard library, libc:
$ nm -g $(find /usr/lib/ -name libc.a) 2>/dev/null | grep 'T getopt'
0000000000001620 T getopt
0000000000000000 T getopt_long
0000000000000040 T getopt_long_only
In C++, the name is not the symbol. A C++ function can be overloaded; the same name can represent different functions taking different parameters. The compiler constructs a symbol that encodes the parameter types. Compare:
$ echo 'int foo(int foo) { return foo * foo; }' > a.c && cc -c a.c && nm a.o
0000000000000000 T foo
$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T _Z3fooi
The encoding of the symbol name is often called name mangling. nm(1) has a demangling feature:
$ echo 'int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm -C a.o
0000000000000000 T foo(int)
Note that the parameter type appears next to the name.
In C++, extern "C"
declares or defines the function as a C function. Here's a definition:
$ echo 'extern "C" int foo(int foo) { return foo * foo; }' > a.C && c++ -c a.C && nm a.o
0000000000000000 T foo
In a declaration, it tells the compiler that the function is defined elsewhere, and to use a C symbol.
In a definition, it tells the compiler to emit a C symbol, i.e. not mangle the name, so that the function can be used by other C modules.
I hope that answers your question.