I have come across this overridden function-call operator() inside a IonizationTunnel.h file:
void operator()(Particles *, std::vector<double>*, unsigned int, int ipart_ref = 0) override;
This matches exactly with the parameters of a virtual void operator() inside a Ionization.h file:
virtual void operator()(Particles *, std::vector<double>*, unsigned int, int ipart_ref = 0) {}
Ionization is the Base class. IonizationTunnel is the Derived class.
2 questions:
What does a parameter with no-name inside the argument list mean? I.e. the pointer to a Particles object, Particles*. Or the unsigned int without a name. Why do they appear like that and what do they mean?
What does the parameter with a name (so an usual parameter) being set to a value inside () mean?
I.e. int ipart_ref = 0. Does it mean that when we'll call IonizationTunnelObject.operator()(arguments) we have to specify 3 arguments and not 4, the last argument (ipart_ref) being silently inferred to be 0 even if not written in the 3 arguments we specify? Or can we call IonizationTunnelObject.operator()(arguments) using 4 arguments and set ipart_ref to any integer value we want?
I am accustomed to seeing foo(int x) {code} and I have never seen foo(int) {code}.
Thank you a lot!
An unused parameter that does not necessarily need a name can be useful sometimes to let overload resolution pick the desired overload:
void foo(int) {
std::cout << "this is foo(int)\n";
std::cout << "I dont need a name for the argument, because I am not using it anyhow";
}
void foo(double) {
std::cout << "this is foo(double)";
}
foo(1); // calls foo(int)
foo(1.0); // calls foo(double)
However, what you see is probably just the names omitted on the declaration and on the definition they will be given names, as in:
void foo(int); // forward declaration
void bar(int x) {
if (x==42) foo(x); // needs a declaration of foo
}
void foo(int x) { // definition
if (x!=42) bar(x);
}
The = something is default arguments. A function
void foo(int x = 0) {
std::cout << x;
}
Can be called either like this:
foo(42);
or like this:
foo();
in which case it is equivalent to calling
foo(0);
Related
void f()
{}
void f(int)
{
return f(); // #1: ok
}
void g(auto fn)
{
f(fn());
}
int g1()
{
return 0;
}
void g2()
{}
int main()
{
g(g1); // #2: ok
g(g2); // #3: error
}
C++ allows explicitly returning a void value as shown at #1, I think it's elegant and generic.
However, the rule cannot be applied to #3 in the same way.
Why does C++ not allow passing a void argument to a function having zero parameters?
Because the language specifies that each argument expression in the call initialises a parameter of the function
[expr.call/7]
When a function is called, each parameter is initialized with its corresponding argument.
In a function of no parameters, there is no first parameter to initialise, and even if there were, void is a type with no values.
class A
{
public:
A() {}
void print(std::function<void(int)> func);
virtual ~A() {}
};
void A::print(std::function<void(int)> func)
{
func();
}
void printInt(int a)
{
std::cout << a << std::endl;
}
int main()
{
A a;
a.print(std::bind(&printInt, 3));
return 0;
}
Let's say I have this sample of code. How to pass void method printInt(int a) to A::print(std::function func) and call there function that has been passed? I get "candidate expects 1 argument, 0 provided" and I don't know how to deal with that.
Edit:
I should have mentioned it but my goal is to pass that integer but actually I have no idea how. When I try something like this:
void A::print(std::function<void(int a)> func)
{
func(a);
}
I get "error: ‘a’ was not declared in this scope".
If you don't intend to pass any arguments to func, it should be std::function<void(void)>. A signature of void(int) means you intent to feed it an integer, something it will expect. And your code clearly doesn't provide the required argument.
If you do intent to pass an argument to func, it needs to be a parameter of A::print, like so:
void A::print(std::function<void(int)> func, int a) {
func(a);
}
Your attempt specified the functions parameter is named a which is moot. Since there is no a in A::print to refer to. The above also means you can dispense with bind:
a.print(&printInt, 3);
Your bind() creates a function that takes no arguments but A::print expects a function that takes 1. It then tries calling that function as if it takes no arguments.
The answer is to change A::print to take a std::function that takes no arguments.
For example, there is a class foo :
class foo {
public:
foo (int = 10);
.....
}
The prototype of the constructor has "int = 10" inside. So, what does it mean? Int is just an integer type, isn't? So, isn't that illegal to assign a value to it? I was trying to find such an example in Prata's book, and everywhere else, but I didn't find the explanation.
You can omit the name of parameter in function declaration (in definition too), but still you are able to specify default value of that parameter.
Consider:
void f(int x = 10) {
printf("%d\n", x);
}
void g(int = 10);
void g(int x) {
printf("%d\n", x);
}
int main() {
f();
g();
return 0;
}
Result:
10
10
The same situation is in the constructor case.
So, isn't that illegal to assign a value to it?
There is absolutely no assignment involved here. The = character can have quite different meanings in C++:
Assignment: i = 0;
Initialisation: int i = 0;
Making a member function pure virtual: virtual void f() = 0;
Specifiying default arguments: void f(int i = 0);
The latter is what you have encountered. A constructor can have default arguments like any other normal function.
§14.8.2/4 allows the instantiation of two different functions, g<int> and g<const int> from the template definition. Why doesn't the Standard allow the definition of the two functions f in the code below? I know that both functions would have the same type void(int). But that also happens with the instantiated functions g. The note in §14.8.2/4 says: f<int>(1) and f<const int>(1) call distinct functions even though both of the functions called have the same function type..
#include <iostream>
template<typename T>
void g(T t) { std::cout << t << '\n'; }
void f(int i) { std::cout << i << '\n'; }
//void f(int const i) { std::cout << i << '\n'; } // doesn't compile
int main()
{
g<int>(1);
g<int const>(2);
}
Top-level consts on the parameter types are not part of the function signature. So the two versions of f() you've defined are the same function as far as overload resolution is concerned making the second one a redefinition.
From §13.1/3 [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. [ Example:
typedef const int cInt;
int f (int);
int f (const int); // redeclaration of f(int)
int f (int) { /* ... */ } // definition of f(int)
int f (cInt) { /* ... */ } // error: redefinition of f(int)
—end example ]
Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.
The fact that top-level const is not part of the function signature allows for a minor advantage.
Suppose you have a function"
void f(int);
in its implementation, if you know you are not going to change the input parameter, you can declare:
void f(int const x) {
std::cout << x << "\n";
}
and this is none of the business of the caller. Later, it turns out it would be useful to munge the input value (say, you want to treat negative integers as 0):
void f(int x) {
if (x<0) x = 0;
std::cout << x << "\n";
}
and without changing the signature or the rest of the body of the function, we are good to go.
Basically, the top level constness of arguments doesn't impact the usual binary calling conventions of C++, and logically the constness is no business of the caller. By eliminating that from the signature, we get some benefit.
For template functions, however, the types impact both the signature and the body of the function, and that body is part of the template's interface. (decltype lets the types of function parameters impact the body, but the body is not part of the interface like a template)
After getting an answer to this question I discovered there are two valid ways to typedef a function pointer.
typedef void (Function) ();
typedef void (*PFunction) ();
void foo () {}
Function * p = foo;
PFunction q = foo;
I now prefer Function * p to PFunction q but apparently this doesn't work for pointer-to-member functions. Consider this contrived example.
#include <iostream>
struct Base {
typedef void (Base :: *Callback) ();
//^^^ remove this '*' and put it below (i.e. *cb)
Callback cb;
void go () {
(this->*cb) ();
}
virtual void x () = 0;
Base () {
cb = &Base::x;
}
};
struct D1 : public Base {
void x () {
std :: cout << "D1\n";
}
};
struct D2 : public Base {
void x () {
std :: cout << "D2\n";
}
};
int main () {
D1 d1;
D2 d2;
d1 .go ();
d2 .go ();
}
But if I change it to the new preferred style: typedef void (Base :: Callback) () and Callback * cb, I get a compiler error at the point of typedef
extra qualification 'Base::' on member 'Callback'
Demo for error.
Why is this not allowed? Is it simply an oversight or would it cause problems?
For non-member functions, a type such as typedef void(Function)() has several uses, but for member functions the only application is to declare a variable which holds a function pointer. Hence, other than a stylistic preference, there's no strict need to allow this syntax and it has been omitted from the standard.
Background
The :: is a scope resolution operator, and the syntax X::Y is reserved for static member access if X is a class type. So X::*Z was another syntax invented to define pointer-to-member.
Forget member-function for a while, just think about member-data, and see this code:
struct X
{
int a;
};
int X::*pa = &X::a; //pointer-to-member
X x = {100}; //a = 100
cout << (x.*pa) << endl;
It defines a pointer-to-member-data, and the cout uses it to print the value of a of object x, and it prints:
100
Demo : http://www.ideone.com/De2H1
Now think, if X::pa (as opposed to X::*pa) were allowed to do that, then you've written the above as:
int X::pa = X::a; //not &X::a
Seeing this syntax, how would you tell if X::a is a static member or non-static member? That is one reason why the Standard came up with pointer-to-member syntax, and uniformly applies it to non-static member-data as well as non-static member-function.
In fact, you cannot write X::a, you've to write &X::a. The syntax X::a would result in compilation error (see this).
Now extend this argument of member-data to member-function. Suppose you've a typedef defined as:
typedef void fun();
then what do you think the following code does?
struct X
{
fun a;
};
Well, it defines member a of type fun (which is function taking no argument, and returning void), and is equivalent to this:
struct X
{
void a();
};
Surprised? Read on.
struct X
{
fun a; //equivalent to this: void a();
};
void X::a() //yes, you can do this!
{
cout << "haha" << endl;
}
We can use exactly the same syntax to refer to a which is now a member-function:
X x;
x.a(); //normal function call
void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member
The similarity is the synatax on the right hand side : &X::a. Whether a refers to a member-function or member-data, the syntax is same.
Demo : http://www.ideone.com/Y80Mf
Conclusion:
As we know that we cannot write X::a on the RHS, no matter if a is a member-data or member-function. The only syntax which is allowed is &X::f which makes it necessary that the target type (on LHS) must be pointer as well, which in turn makes the syntax void (X::*pa)() absolutely necessary and fundamental, as it fits in with other syntax in the language.
To be precise the two typedef's in the case of the non-member pointers are not the same:
typedef void function();
typedef void (*fptr)();
The first defines function as a function taking no arguments and returning void, while the second defines ftpr as a pointer to function taking no arguments and returning void. The confusion probably arises as the function type will be implicitly converted to a pointer type in many contexts. But not all:
function f; // declares void f();
struct test {
function f; // declares void test::f()
};
void g( function f ); // declares g( void (*f)() ): function decays to pointer to function in declaration
g( f ); // calls g( &f ): function decays to pointer to function
void f() {} // definition of f
// function h = f; // error: cannot assign functions
function *h = f; // f decays to &f
Let's skip the "function" part for a second. In C++, we have the int, the int* and the int Foo::* types. That's a regular integer, pointer to integer, and a pointer to an integer member. There is no fourth type "integer member".
Exactly the same applies to functions: there's just no type "member function", even though there are function types, function pointer types, and member function pointer types.