Given a function in C++ with arguments that are only types and have no identifiers,
void foo1(int, int, int){cout << "called foo1";}
I can call it as such:
int main()
{
foo1(10, 10, 10);
}
Why is this a valid construct in C++? Is this just an idiosyncrasy of C++, or does this kind of declaration actually have some purpose? Can we actually access the arguments that have been passed in somehow?
(This kind of method declaration will not work in Java.)
Consider a case where you are required to provide a function that meets the following prototype
void dostuff(int x, int y, int z);
And say you are operating in a 2D space and do not use z inside your implementation. You can
void dostuff(int x, int y, int z)
{
// use x and y
}
and just ignore z, but the compiler will likely spot that you have defined but not used z and warn you that you could be making a mistake. Instead you can
void dostuff(int x, int y, int )
{
// use x and y
}
and leave out the definition of z. The compiler will accept and silently discard the third parameter because it knows you don't want it.
You do not want to simply turn off the warning because of errors like this
void dostuff(int x, int y, int z)
{
for (int z = 0; z < MAX; z++)
{
// use x and y and z, the local z.
}
}
Where a poorly-named loop index shadows the parameter z. The caller's input is now ignored and this could have bad consequences. This error is often hard to spot with the mark 1 eyeball, especially if the local z is buried somewhere deep in a complex function.
Anytime the compiler can pick off a possible bug in your code, take advantage. It means less work for you.
A declaration like
void foo1(int, int, int){cout << "called foo1";}
clearly shows in the declaration, that you want to fulfill a requirement with your function - e.g. override a specific function in the base class or interface, which e.g. could be declared there as
virtual void foo1(int something, int another, int andAnother) = 0;
BUT you don't intend to use the parameters which are handed over to you.
Another example would be if you want to hand over the function to e.g. another function which expects a function pointer to a void function with three int parameters.
void giveMeFoo( void (*fn)(int, int, int) ) { ... }
Additionally, higher warning levels issue a warning, if parameters are declared, which are not evaluated in the function body. You can avoid that by leaving the parameter names away.
The parameters without names are then indeed not accessible in the function body anymore - on purpose. user4581301 has nicely described, why.
Declaring a standalone function without parameter names as in your example is allowed because of the usages described above, but it obviously makes no sense in most cases. An example where it does make sense is in the comment section. Another example of a standalone function without parameter names could be, if your'e writing a library and want to either maintain backward compatibility (your library function does not need the parameter anymore, but you don't want to break the public header declaration) or you want to reserve a parameter for future use.
Yes. It is legal in C++.
C++11 n3337 standard 8.4.1(p6) Function definitions:
Note: Unused parameters need not be named. For example,
void print(int a, int) {
std::printf("a = %d\n", a);
}
C++14 standard:
[ 8.3.5.11] An identifier can optionally be provided as a parameter name; if present in a function definition , it names a parameter
(sometimes called “formal argument”). [Note: In particular, parameter
names are also optional in function definitions and names used for a
parameter in different declarations and the definition of a function
need not be the same.]
It's legal, and if you're wondering why:
Typically, unnamed arguments arise from the simplification of code or
from planning ahead for extensions. In both cases, leaving the
argument in place, although unused, ensures that callers are not
affected by the change.
Excerpt From: Bjarne Stroustrup. “The C++ Programming Language, Fourth Edition.”
The idea is that you might want to change the function
definition to use the placeholder later, without changing all the
code where the function is called.
Arguments in a function declaration can be declared without
identifiers. When these are used with default arguments, it can look
a bit funny. You can end up with :
void f(int x, int = 0, float = 1.1);
In C++ you don’t need identifiers in the function definition, either:
void f(int x, int, float flt) { /* ... */ }
In the function body, x and flt can be referenced, but not the
middle argument, because it has no name. Function calls must still
provide a value for the placeholder, though: f(1) or f(1,2,3.0). This
syntax allows you to put the argument in as a placeholder without
using it.
Unnamed parameters can also be used for the tag dispatch idiom:
template<typename T>
void foo(T&& t){
foo(std::forward<T>(t), std::is_same_v<std::remove_ref<std::remove_const<T>>>, std::string>); // do something special with strings
template<typename T>
void foo(T&& t, std::false_type); // note no parameter name
template<typename T>
void foo(T&& t, std::true_type);
Related
The title is a bit lengthy, but it's best explained by an example:
Suppose we have the following functions in C++:
void SomeFunction(int num) { //1
}
void SomeFunction(int& num) { //2
}
void SomeFunction(const int& num) { //3
}
void SomeFunction(const int num) { //4
}
All of these are called the same way:
SomeFunction(5);
or
int x = 5;
SomeFunction(x);
When I tried to compile the code, it rightfully says more than one instance of overloaded function "SomeFunction" matches the argument
My question is: Is there a way to tell the compiler which function I meant to call?
I asked my lecturer if it was possible, and she tried something along
SomeFunction< /*some text which I don't remember*/ >(x);
But it didn't work and she asked me to find out and tell her.
I also encounter this post:
How to define two functions with the same name and parameters, if one of them has a reference?
And it seems that 1 and 2 can't be written together, but what about 3 and 4? Can either one of those be called specifically?
1 and 4 have the same signature, so you'll need to drop one of those.
The other functions cannot be called directly, but you could add a template function that allows you to specify the desired parameter type:
template<class Arg>
void Call(void f(Arg), Arg arg)
{
f(arg);
}
// Driver Program to test above functions
int main()
{
int i;
Call<int>(SomeFunction, 1);
Call<int&>(SomeFunction, i);
Call<const int&>(SomeFunction, 1);
}
Alternatively you could use a function pointer to choose the signature.
int i;
static_cast<void(*)(int)>(&SomeFunction)(1);
static_cast<void(*)(int&)>(&SomeFunction)(i);
static_cast<void(*)(const int&)>(&SomeFunction)(1);
It would be preferrable to avoid this scenario though and only define overloads for either references or the signature void SomeFunction(int).
Note:
SomeFunction<some text which I don't remember>(x);
only works for template functions and SomeFunction is not a template function, so this is not an option here.
You can HACK it, and I mean it - it's not a good solution to your problem, by static casting your function explicitly:
static_cast<void(*)(int)>(SomeFunction)(i);
static_cast<void(*)(int&)>(SomeFunction)(i);
static_cast<void(*)(const int&)>(SomeFunction)(i);
Demo
It will work for first 3 overloads. 4th one is equivalent to 1st: quote from the standard [over.load]:
Parameter declarations that differ only in the presence or absence of const and/or volatile are
equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when
determining which function is being declared, defined, or called
and there is an example:
int f (int);
int f (const int); // redeclaration of f(int)
Also note that you cannot call 2nd overload with rvalue (temporary).
The only way I see this working the way your lecturer tried is if SomeFunction is a template and these four overloads are specializations.
template<typename T>
void SomeFunction(T num);
template<>
void SomeFunction<int>(int num) {}
template<>
void SomeFunction<int&>(int& num) {}
template<>
void SomeFunction<const int&>(const int& num) {}
template<>
void SomeFunction<const int>(const int num) {}
Then you can call it as follows.
SomeFunction<int>(x);
SomeFunction<int&>(x);
SomeFunction<const int&>(x);
SomeFunction<const int>(x);
Demo
However, this is incredibly stupid in this context. There are a lot of things wrong with the original overloads.
In the 4th one, the const is completely useless from the caller's perspective, because you can call it the same way you can call the 1st, and the argument is a copy anyway. The const only makes it so the argument is constant inside the function. Moreover, the 1st and 4th overloads cannot both be defined at the same time: the const is actually ignored in the prototype and it leads to a redefinition.
The 3rd overload is also useless because a const int& argument provides no benefit over a int argument. In fact, the compiler probably optimizes that away. The only difference is in the scenario I describe at the end. Of course, if the argument type is more complex (not just int or some other fundamental type), it often makes sense.
The 2nd overload is the only one that can modify the variable you pass as argument. However, if the 1st (or 4th, since it's really the same) overload is present as well, you cannot call the 2nd directly because the call would be ambiguous. You could still call the 1st with an rvalue (basically a literal or an expression like std::move(x)).
If the 2nd and 3rd overloads are the only ones present, then there is no ambiguity and you can call the 2nd with non-const lvalues and the 3rd with const lvalues or rvalues.
Demo
I "wanted" to use void as a placeholder (or overload disambiguator) or even as a shortcut to have functions with void return type called before entering a specific function like in the following example:
int f(void , int)
{
return 0;
}
void g()
{
}
int main()
{
f(g(), 1);
}
Now, this is not a real world problem (I know that I could just call g() before calling f()) but I was wondering why this is not doable, especially when I can e.g. explicitly return void types i.e. this is legal :
void h()
{
return g(); // this does a return void
}
EDIT
To explain the rationale behind asking this, I first thought that according to C legacy, void would be an incomplete type, so incomplete types cannot appear as function parameters, unlike pointers to incomplete types and hence the void* commonality. Now this would explain void as a "special case" signal for "no parameters" but after C++11 the Standard reads (3.9 [basic.types]) :
A type is a literal type if it is:
void; or
a scalar type; or
....
Being a literal type, I can't find elsewhere any rationale that would exclude void from candidate types for function parameters, neither the equivalent of old C's (prior to C11) "void is not a type". Now, my search may be lacking the required depth which is what I try to compensate for in this Q
A void parameter means the function has no parameters*. It wouldn't make much sense for a function with no parameters to have some parameters.
* This is inherited from C (and presumably kept for compatibility with that language), where a function declared without a parameter list is a function that can take any number of parameters of any type. In C++, such a function would have no parameters, removing the need to use void parameters.
The only real problem here is your function prototype:
int f(void , int)
You cannot give a void as a parameter. You can set it as a return value, meaning "this function returns nothing", or you can give it as only parameter, like this:
int f(void)
It would means "this function takes no parameter", but not as a parameter.
But to give a parameter of void type would mean you could declare a void variable and give it to your function, which would have no sense.
In your sample:
void h()
{
return g(); // this does a return void
}
This does not a return void. This does return nothing. This is as legal as:
void h()
{
return;
}
So here, you can clearly see void is just a meaning of nothing.
Try to use functions returning void as arguments, like you did:
f(g(), 1);
Should be avoided as much as possible.
I've wanted a void argument type in order to have a parameter that is zero-cost in release builds:
#ifdef NDEBUG
typedef DebugTracker* Foo;
#else
typedef void Foo;
#endif
int SomeFunction(Foo foo, ...) {
...
}
I can't find elsewhere any rationale that would exclude void from candidate types for function parameters
#juanchopanza has pointed out one thing, which is that C++ inherited C's f(void) meaning a function that takes no arguments. That being so, C++ still could have the feature but make void parameters act as if they had a default value of nothing...so having such a default if they were at the end of argument lists.
In language-design space, it's always easy to think of the case you have in mind and say "why not?". And if you look at something like libffi then it seems like prohibiting void for arguments makes the system less "pure". There's a count of bytes for each argument, how hard would it be to allow 0?
But there are questions to answer.
If void parameters are possible, then that posits the existence of void variables. How does a void variable act? What's its address? If you can't take the address of a void variable, how does that impact the compiler...the linker...what's going to happen with name-mangling, etc.
I don't know enough to tell you if the pretzel of the existing C and C++ standard can be untwisted in a way that void parameters do more good than harm. It would be an interesting study to take a compiler and some large body of code and think through the details. I upvoted the question as reasonable to ask, but also voted to close as primarily opinion-based, so... that's my 0.02.
I recently found out that you can add and remove cv-qualifier from value arguments in a function declaration and it's implementation. For instance:
F.h
f(const int i);
F.cpp
f(int i) { i++; }
test.cpp
#include "F.h"
f(0);
The f(int i) implementation will be called. Since its a copy by value, I see no problems with the code compiling. But can anyone imagine a case, where it's somehow useful to add the const to the declaration as done above? Why is it possible at all? Maybe someone can give a more useful application?
Top-level CV qualifiers on function arguments are ignored, i.e. they are not part of the function signature. Rather, if you will, they are part of the implementation of the function, since they qualify the local variables corresponding to the formal function parameters. Therefore, if it pleases you, you may qualify function parameters in the function definition:
int power(int val, int n, int base);
int power(int val, int n, const int base)
{
while (n --> 0) val *= base;
return val;
}
Some people find it offensive if the definition has different-looking function parameters from the declaration, though. It's a matter of taste and style. The benefits of qualifying the arguments are probably small, though there is some value for the implementation in documenting that you don't plan to change a variable.
Some people like to state in their definition that they are not going to change a parameter even though it is only visible locally. For example:
void f(int);
void f(int const);
void f(int const x) {
x = 3; // ERROR!
}
The definition is the function which was just declared, i.e., there is no change on the signature (well, unless you happen to use Sun's CC which actually takes the const into account for the mangled name but that's just an error). All three signatures declare exactly the same function and the const only affects the body of the definition if it is used in the definition's declaration.
I want to declare type definition for a member function signature. Global function typedefs look like this:
typedef int (function_signature)(int, int);
typedef int (*function_pointer) (int, int);
But I'm not able to the same thing for a member function:
typedef int (foo::memberf_signature)(int, int); // memberf_pointer is not a member of foo
typedef int (foo::*memberf_pointer)(int, int);
It sounds logically to me, because foo:: is the syntax to access a member in the class foo.
How can I typedef just the signature?
For questions regarding the awkward function pointer syntax, I personally use a cheat-sheet: The Function Pointers Tutorial (downloadable here, thanks to Vector for pointing it out).
The signature of a member function, however, is a bit different from the signature of a regular function, as you experienced.
As you probably know, a member function has a hidden parameter, this, whose type need be specified.
// C++11 and above.
using Member = int (Foo::*)(int, int);
// C++03 and below.
typedef int (Foo::*Member)(int, int);
does let you specify that the first element passed to the function will be a Foo* (and thus your method really takes 3 arguments, when you think of it, not just 2.
However there is another reason too, for forcing you to specify the type.
A function pointer might refer to a virtual function, in which case things can get quite complicated. Therefore, the very size of the in-memory representation changes depending on the type of function. Indeed, on Visual Studio, a function pointer's size might vary between 1 and 4 times the size of a regular pointer. This depends on whether the function is virtual, notably.
Therefore, the class the function refers to is part of the signature, and there is no work-around.
You can factor out the target class in modern C++ (post 11) by utilizing the 'typedefing' qualities of template aliases. What you need would look like like:
template<typename T>
using memberf_pointer = int (T::*)(int, int);
Yet at the point of declaration, a pointer to member function utilizing this syntax would need to specify the target class:
// D is a member function taking (int, int) and returning int
memberf_pointer<foo> mp = &foo::D;
The reason it doesn't work with your current syntax is that operator precedence dictates that you're referring to a function named foo::memberf_signature, not any sort of type.
I don't know for sure if you can do this or not, but I couldn't come up with any combination of parenthese that induced the code to compile with g++ 4.2.
It works for me:
#include <iostream>
class foo
{
public:
int g (int x, int y) { return x + y ; }
} ;
typedef int (foo::*memberf_pointer)(int, int);
int main()
{
foo f ;
memberf_pointer mp = &foo::g ;
std::cout << (f.*mp) (5, 8) << std::endl ;
}
Well basically it can't work (at least I know no way using g++);
Using borland c++ compiler there would be the __closure keyword.
The reason why it does not compile is, that sizeof the functionpointer (on a x86 machine) occupies always <<32bits>>; but if you want to point to a class (interface) signature, the sizeof has to be 64bit: 32 bit for the this pointer (as the class interface is in the memory only once) and 32 bit for the actual function
But the __closure keyword is a bcb language 'hack' not standardized...
What does the C++ compiler do when coming ambiguous default parameters? For example, let's say there was a function such as:
void function(int a = 0, float b = 3.1);
void function(int a, float b =1.1, int c = 0);
Is the above considered ambiguous? If not, what does the compiler do (how is the function matched exactly) when calling something like function1(10) ?
Thanks!
The following is fine
void function(int a = 0, float b = 3.1);
void function(int a, float b =1.1, int c = 0);
And the following is fine too
function(); // calls first function
But the following is ambiguous
function(1); // second and first match equally well
For overload resolution (the process that tells what function to call), parameters that have not passed explicit arguments and that make use of default arguments are ignored. So the compiler really sees two functions both having one int parameter for the above call and can't decide.
The following is ill-formed though
void function(int a = 0, float b = 3.1);
void function(int a, float b =1.1);
While for the code in your question you declare two functions (because both declarations have different number of parameters), in this example you only declare one function. But the second declaration of it repeats a default argument for a parameter (and even with a different value, but that doesn't matter anymore). This is not allowed. Note that the following is fine
void function(int a, float b = 3.1);
void function(int a = 0, float b);
The set of default arguments for declarations that appear in the same scope for the same function are merged, and only for those that appear in the same scope. So the following is valid
void function(int a = 0, float b = 3.1);
void function1() {
void function(int a, float b = 1.1);
function(0);
}
This calls the function with 1.1 passed for b.
If they have different names (as in your example), there's no ambiguity. If they have the same name (so it's an attempt at an overload), the compiler will complain.
Though it turns out you can redefine the default arguments to a function in a different scope (this is news to me...) - but in the same scope, you can't redefine default arguments even to the same value. from 8.3.6/4 "Default arguments":
For non-template functions, default
arguments can be added in later
declarations of a function in the same
scope. Declarations in different
scopes have completely distinct sets
of default arguments. That is,
declarations in inner scopes do not
acquire default arguments from
declarations in outer scopes, and vice
versa. In a given function
declaration, all parameters subsequent
to a parameter with a default argument
shall have default arguments supplied
in this or previous declarations. A
default argument shall not be
redefined by a later declaration (not
even to the same value).
Ambiguous? You have two completely independent different functions: function1 and function2. Even the number of parameters in each function is different. There's no ambiguity here whatsoever. When you ask the compiler to call function1(10) it calls function1(10, 3.1). function2 doesn't even come into the picture.
If it were the same function, then the issue of ambiguity would not arise simply because it is illegal in C++ to specify a default argument for the same parameter more than once (within the same translation unit). Even of you specify the same default argument value the second time, the program is ill-formed
void foo(int a = 5);
void foo(int a = 5); // <- ERROR
What one can do though is to specify a different set of default arguments for the same function in different translation units. But that does not create any ambiguity because the compiler can see only one translation unit. The compiler simply will never know of any potential "ambiguity" is that case.
Furthermore, the answer to any question starting with, "What does the C++ compiler do...," is always going to be, "Depends on which compiler you're talking about."