I was writing a function foo() which takes 2 const char*s as arguments, pBegin and pEnd. foo() is passed a null terminated string. By default pEnd points to \0 (last character) of the string.
void foo (const char *pBegin,
const char *pEnd = strchr(pBegin, 0)) // <--- Error
{
...
}
However, I get an error at above line as:
error: local variable ‘pBegin’ may not appear in this context
Why compiler doesn't allow such operation ? What's the potential problem ?
The standard not only explicitly disallows the use of other parameters in a default argument expression, but also explains why and gives an example:
ISO/IEC 14882:2003(E) - 8.3.6 Default arguments [dcl.fct.default]
9. Default arguments are evaluated each time the function is called.
The order of evaluation of function arguments is unspecified.
Consequently, parameters of a function shall not be used in default
argument expressions, even if they are not evaluated. Parameters of a
function declared before a default argument expression are in scope
and can hide namespace and class member names. [Example:
int a;
int f(int a, int b = a); // error: parameter a
// used as default argument
typedef int I;
int g(float I, int b = I(2)); // error: parameter I found
int h(int a, int b = sizeof(a)); // error, parameter a used
// in default argument
—end example] ...
The language still offers a way to do what you want - use overloaded functions:
void foo (const char *pBegin, const char *pEnd)
{
//...
}
void foo (const char *pBegin)
{ foo(pBegin, strchr(pBegin, 0)); }
When the function is called the default arguments are evaluated, but the order they are evaluated is not defined by the C++ standard. That means that you can't reference other parameters in a default argument because they may not have a known value yet.
You can't use a local variable in a default argument value.
Quote from here:
The standard tells us that the default argument is simply an expression.
There are some things that are no allowed (using local variables, using
the keyword 'this') but pretty much anything else can serve as a default
argument.
Related
How can I receive in a function definition when the one of the parameter in function call is void?
The scenario is that one of my projects, in a function call one of the parameters is void. That section I could not change.
I can change only the function declaration or the function definition, but I still confused what I need to change.
Please help
Below code is not a executable one:
for ex:
#include <stdio.h>
void test(void, int a) {
printf("%d", a);
}
int main() {
test(void, 32);
}
You can't. That is not valid C:
<source>:2:11: error: 'void' must be the only parameter
2 | void test(void,int a)
| ^~~~
Same goes for C++:
<source>:2:11: error: invalid use of type 'void' in parameter declaration
2 | void test(void,int a)
| ^~~~
The only valid use of void as parameter is on it's own, as in:
int bar();
int foo(void);
In C this means the function bar takes an undetermined number of arguments while foo does not take any arguments. In C++ both are equivalent.
void is an incomplete type and cannot be used as the parameter of a function(in both C and C++) with the exception that if void is the only parameter of that function. This can be seen from the following quoted statements.
C++
From function's documentation:
Parameter list determines the arguments that can be specified when the function is called. It is a comma-separated list of parameter declarations, each of which has the following syntax:
5 Indicates that the function takes no parameters, it is the exact synonym for an empty parameter list: int f(void); and int f(); declare the same function. Note that the type void (possibly cv-qualified) cannot be used in a parameter list otherwise: int f(void, int); and int f(const void); are errors (although derived types, such as void* can be used). In a template, only non-dependent void type can be used (a function taking a single parameter of type T does not become a no-parameter function if instantiated with T = void).
Note also that there is a semantic difference(between C++ & C) in case void is used as a parameter of a function and is the only parameter as shown in the following example:
int func(void); //declaration of func that takes no parameter and has the return type of void
int foo(); //declaration of foo that takes no parameter and has the return type of void
C
From function declaration's documentation:
parameters cannot have type void (but can have type pointer to void). The special parameter list that consists entirely of the keyword void is used to declare functions that take no parameters.
int f(void); // OK
int g(void x); // Error
Unlike in C++ and function definitions (since C23), the declarators f() and f(void) have different meaning: the declarator f(void) is a new-style (prototype) declarator that declares a function that takes no parameters. The declarator f() is a declarator that declares a function that takes unspecified number of parameters (unless used in a function definition)
I know that you can declare a function without any arguments simply like this:
void test()
{
cout << "Hello world!!" << endl;
}
But I have also seen
void test(void)
{
cout << "Hello world!!" << endl;
}
and
void test(void*)
{
cout << "Hello world!!" << endl;
}
My question is: What is the difference between using void and void* here?
The one an only form for a function with no parameters in C++ should be
void test();
The form:
void test(void)
That is how you define a function with no parameters in c. But in C++ use this form only for interfacing with c:
extern "C"
{
void test(void);
}
This form:
void test(void*)
Is not a function with no arguments. It has an argument of type void* that is unnamed. It expects a void* pointer when called.
Have a look here for an explanation of what a void* pointer is: What does void* mean and how to use it?
What is the difference between using void and void* here?
When the argument type is void, the function must be called without any arguments.
When the argument type is void*, the function must be called with an argument that is a void* or can be converted to a void*.
void stands for "nothing" but void* does not stand for "pointer to nothing". In fact, it's quite the contrary. void* stands for pointer to anything except void. It is oxymoronic but that's how the language defines those two.
My question is: What is the difference between using void and void* here?
(void) parameter list is same as empty parameter list. The void here does nothing in C++ and is unnecessary unless compatibility with C is needed - see the second part of the answer.
(void*) does not accept 0 arguments. It accepts 1 argument. The type of the parameter is void* which is a pointer type.
Why is it like this?
Because of backwards compatibility with C. In C, (void) is the only way to declare a function that only accepts empty argument list. () parameter list declares any function without specifying the parameters. The number of parameters could be any, including 0. The use of () parameter list in C is becoming an obsolete feature.
Quote from C11 standard draft N1548:
Function declarators (including prototypes)
An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.
Future language directions
Function declarators
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
Function definitions
The use of function definitions with separate parameter identifier and declaration lists (not prototype-format parameter type and identifier declarators) is an obsolescent feature.
C is this way because of backward compatibility with older versions (pre-standard) of C where parameter lists weren't declared.
In C++ these two declarations of a function
void test();
and
void test(void);
are equivalent and mean that the function does not take any arguments.
In C, the first function declaration declares a function with an empty identifier list, and the second function declaration declares a function with a parameter type list that in fact has no parameters.
According to the C Standard (6.9.1 Function definitions):
5 If the declarator includes a parameter type list, the declaration of
each parameter shall include an identifier, except for the special
case of a parameter list consisting of a single parameter of type
void, in which case there shall not be an identifier. No declaration
list shall follow.
This declaration of the function (that is also its definition)
void test(void*)
{
//...
}
has 1 parameter, an object of type void * (pay attention to that pointers are always complete types), that is not used within the function.
This declaration is valid in C++, and is not valid in C, because in C each function parameter in a function declaration with a parameter type list that is at the same time its definition shall have an identifier, except in the case when a void parameter is used, as specified in the quote above.
In C you may use such a declaration only when the declaration is not at the same type its definition.
So in C for example you may write like
void test(void*); // declaratipon
and then
void test( void *ptr ) // declaration and definition
{
//...
}
Consider the following constructor:
class MyClass {
MyClass(unsigned int dimension, std::vector vector=unitaryVector(dimension));
};
where unitaryVector(d) is a function that returns a random std::vector in d dimensions.
This gives the following compiler error:
error: default argument references parameter 'dimension'
MyClass(unsigned int dimension, std::vector vector=unitaryVector(dimension));
Why isn't this idiom valid in C++11? It seems to be pretty obvious: if vector argument is provided, init vector as a copy of the argument, otherwise, call the function and init it as a copy of the return value. Why can't a compiler understand this?
The C++ standard forbids it.
dcl.fct.default
9 default argument is evaluated each time the function is called with
no argument for the corresponding parameter. A parameter shall not
appear as a potentially-evaluated expression in a default argument.
Parameters of a function declared before a default argument are in
scope and can hide namespace and class member names.
[ Example:
int a;
int f(int a, int b = a); // error: parameter a
// used as default argument
typedef int I;
int g(float I, int b = I(2)); // error: parameter I found
int h(int a, int b = sizeof(a)); // OK, unevaluated operand
— end example ]
Note that default arguments are replaced at the call site if not provided
Intro.execution (emphasis mine)
11: [ Note: The evaluation of a full-expression can include the evaluation of subexpressions that are not lexically part of the
full-expression. For example, subexpressions involved in evaluating
default arguments ([dcl.fct.default]) are considered to be created
in the expression that calls the function, not the expression that
defines the default argument. — end note ]
You can simply overload the constructor and delegate it:
class MyClass {
explicit MyClass(unsigned int dimension)
: MyClass(dimension, unitaryVector(dimension)) //delegation
{ }
MyClass(unsigned int dimension, std::vector vector);
};
Footnote: Its a good thing to make single argument constructors explicit
One alternative is to use
class MyClass {
MyClass(unsigned int dimension, std::vector const& vector) :
dimension(dimension), vector(vector) {}
MyClass(unsigned int dimension) :
MyClass(dimension, unitaryVector(dimension)) {}
};
(this of course when you want to store dimension and vector in the class).
Because the default argument must be complete in itself so that compiler can simply replace it if not provided by call. The (local) variable dimension isn't created yet and you are trying to use it, and hence the error. This would work, however:
int _def_dim=10;
class MyClass {
MyClass(unsigned int dimension, std::vector vector=unitaryVector(_def_dim));
};
I am not sure what standard says, but for compiler implementation is would be tricky to handle such corner cases.
EDIT (for completeness), grabbed from this answer:
Default arguments are evaluated each time the function is called. The
order of evaluation of function arguments is unspecified.
Consequently, parameters of a function shall not be used in default
argument expressions, even if they are not evaluated.
This question already has answers here:
Can I set a default argument from a previous argument?
(7 answers)
Closed 5 years ago.
For a default argument in C++, does the value need to be a constant or will another argument do?
That is, can the following work?
RateLimiter(unsigned double rateInPermitsPerSecond,
unsigned int maxAccumulatedPermits = rateInPermitsPerSecond);
Currently I am getting an error:
RateLimiter.h:13: error: ‘rateInPermitsPerSecond’ was not declared in this scope
Another argument cannot be used as the default value. The standard states:
8.3.6 Default arguments...
9 A default argument is evaluated each time the function is called with no argument for the corresponding
parameter. The order of evaluation of function arguments is unspecified. Consequently, parameters of a
function shall not be used in a default argument, even if they are not evaluated.
and illustrates it with the following sample:
int f(int a, int b = a); // error: parameter a
// used as default argument
No, that cannot work because the evaluation of function arguments is not sequenced. It also does not work because the standard does not allow it, but I guess that was obvious.
Use an overload instead:
void fun(int, int) {}
void fun(int i) {
fun(i, i);
}
I was looking for an logical explanation for why it is not allowed
This is actually a good question. The reason is that C++ does not mandate the order of evaluation of arguments.
So let's imagine a slightly more complex scenario:
int f(int a, int b = ++a);
... followed by ...
int a = 1;
f(a);
C++ does not mandate the order of evaluation of arguments, remember?
So what should be the value of b?
f(a) can evaluate to either:
f(1, 2), or
f(2, 2), depending on the order of evaluation of the arguments.
Thus the behaviour would be undefined (and even undefinable).
Further, consider what might happen when a and b were complex objects whose constructors and copy operators had side-effects.
The order of those side-effects would be undefined.
You cannot do things like that because the standard does not allow it. However since default arguments effectively just define new function overloads, you can get the desired effect by explicitly defining such an overload:
void RateLimiter(unsigned int rateInPermitsPerSecond,
unsigned int maxAccumulatedPermits);
inline void RateLimiter(unsigned int rateInPermitsPerSecond)
{ return RateLimiter(rateInPermitsPerSecond,rateInPermitsPerSecond); }
This shows that the standard forbidding this is half-hearted, as suggested by the language ("Consequently... shall not..."). They just did not want to go through the hassle of making this well defined with the same effect as what the explicit overload declaration would do: if desired, they could have specified that defaulted arguments are evaluated after explicitly provided ones, and from left to right. This would not have any influence on the rule that the evaluation order of argument expressions in a function call is unspecified (because default arguments do not correspond to such expressions; they are entirely separate and not even in the same lexical scope). On the other hand if (as they did) they preferred to disallow this, they could have just said "shall not" without need to justify themselves from some other rule (but maybe with explanatory footnote).
For a default argument in C++, does the value need to be a constant or will another argument do?
The default value of an argument cannot be another argument. However, that does not mean it has to be a constant. It can be the return value of a function call.
int getNextDefaultID()
{
static int id = 0;
return ++id;
}
struct Foo
{
Foo(int data, int id = getNextDefaultID()) : data_(data), id_(id) {}
int data_;
int id_;
};
int main()
{
Foo f1(10); // Gets the next default ID.
Foo f2(20, 999); // ID is specified.
}
The following code compiles cleanly with GCC:
void func(int arg1, decltype(arg1) arg2)
{
(void)arg2;
}
int main(){}
I used this command to compile:
g++ -std=c++14 test.cpp -o test -pedantic-errors -Wall -Wextra
But such usage of a parameter in the middle of function declaration seems weird. Is it actually valid in standard C++, or is it a GCC extension?
This is fine. The ISO C++11 Standard even gives your situation as an example.
First the parameter is in scope:
3.3.3 Block scope [ basic.scope.local ]
2 The potential scope of a function parameter name (including one appearing in a lambda-declarator) or of
a function-local predefined variable in a function definition (8.4) begins at its point of declaration.
An example can be found here:
8.3.5 Functions [ dcl.fct ]
5 [ Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. — end note ]
Yes, it's legal. It's basically just a question of scope. From [basic.scope.block]:
The potential scope of a function parameter name (including one appearing in a lambda-declarator) or of a function-local predefined variable in a function definition (8.4) begins at its point of declaration.
The scope of arg1 begins here:
void func(int arg1, decltype(arg1) arg2)
------------------^
Hence arg1 is in scope for the declaration of arg2. I think that's sufficient.
The rule for disallowing defaulting arg2 to arg1 is separate -- which to me suggests that arg1 was in scope and had to be explicitly disallowed.
If we look in N3979 [dcl.fct.default] we have
Default arguments are evaluated each time the function is called. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in a default argument, even if they are not evaluated. Parameters of a function declared before a default argument are in scope and can hide namespace and class member names. [ Example:
int a;
int f(int a, int b = a); // error: parameter a
// used as default argument
typedef int I;
int g(float I, int b = I(2)); // error: parameter I found
int h(int a, int b = sizeof(a)); // error, parameter a used
// in default argument
[...]
Emphasis mine
So in the example a is known when we get to b and it hides the a from the calling scope. This leads me to believe each function parameter is known before each subsequent parameter. This means you should be able to use its type. You cannot use its value - as the order of evaluation of the values is unspecified - but the names should be introduced in order from left to right.