Parameters: Local variable declared inside the function parameter list. they are initialized by the arguments provided in the each function call.
Arguments: Values supplied in a function call that are used to initialize the function's parameters.
(a) string f() { // return should be string, not int
string s;
// ...
return s;
}
(b) void f2(int i) { /* ... */ } // function needs return type
(c) int calc(int v1, int v2) { /* ... */ } // parameter list cannot use same name twice
(d) double square (double x) { return x * x; } // function body needs braces
#include <iostream>
int fact(int i)
{
if(i<0)
{
runtime_error err("Input cannot be a negative number");
cout << err.what() << endl;
}
return i > 1 ? i * fact( i - 1 ) : 1;
}
int main()
{
std::cout << std::boolalpha << (120 == fact(5)) << std::endl;
return 0;
}
#include <iostream>
#include <string>
int fact(int i)
{
return i > 1 ? i * fact(i - 1) : 1;
}
void interactive_fact()
{
std::string const prompt = "Enter a number within [1, 13) :\n";
std::string const out_of_range = "Out of range, please try again.\n";
for (int i; std::cout << prompt, std::cin >> i; )
{
if (i < 1 || i > 12)
{
std::cout << out_of_range;
continue;
}
std::cout << fact(i) << std::endl;
}
}
int main()
{
interactive_fact();
return 0;
}
#include <iostream>
int abs(int i)
{
return i > 0 ? i : -i;
}
int main()
{
std::cout << abs(-5) << std::endl;
return 0;
}
local variable: Variables defined inside a block;
parameter: Local variables declared inside the function parameter list
local static variable: local static variable(object) is initialized before the first time execution passes through the object’s definition.Local statics are not destroyed when a function ends; they are destroyed when the program terminates.
// example
size_t count_add(int n) // n is a parameter.
{
static size_t ctr = 0; // ctr is a static variable.
ctr += n;
return ctr;
}
int main()
{
for (size_t i = 0; i != 10; ++i) // i is a local variable.
cout << count_add(i) << endl;
return 0;
}
size_t generate()
{
static size_t ctr = 0;
return ctr++;
}
Exercise 6.9 fact.cc | factMain.cc
void f(T)
pass the argument by value. nothing the function does to the parameter can affect the argument.
void f(T&)
pass a reference, will be bound to whatever T object we pass.
a parameter should be a reference type:
void reset(int &i)
{
i = 0;
}
a parameter should not be a reference:
void print(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
for (std::vector<int>::iterator iter = begin; iter != end; ++iter)
std::cout << *iter << std::endl;
}
why is
s
a reference to const butoccurs
is a plain reference?
Because s
should not be changed by this function, but occurs
result must be calculated by the function.
Why are these parameters references, but the char parameter
c
is not?
Because c
may be a temp varable, such as find_char(s, 'a', occurs)
What would happen if we made
s
a plain reference? What if we madeoccurs
a reference to const?
s
could be changed in the function, and occurs
would not be changed. so occurs = 0;
is an error.
bool is_empty(const string& s) { return s.empty(); }
Since this function doesn't change the argument, "const" shoud be added before string&s, otherwise this function is misleading and can't be used with const string or in a const function.
Not the same. For the first one "const" was used, since no change need to do for the argument. For the second function, "const" can't be used, because the content of the agument should be changed.
(a)
bool compare(const matrix &m1, cosnt matrix &m2);
(b)
vector<int>::iterator change_val(int num, vector<int>::iterator &it);
(a) illegal, only one parameter. (b) legal. (c) legal. (d) legal.
If we can use const
, just use it. If we make a parameter a plain reference when it could be a reference to const
,
the reference value maybe changed.
Arrays have two special properties that affect how we define and use functions that operate on arrays: We cannot copy an array, and when we use an array it is (usually) converted to a pointer.
So we cannot pass an array by value, and when we pass an array to a function, we are actually passing a pointer to the array's first element.
In this question, const int ia[10]
is actually same as const int*
, and the size of the array is irrelevant.
we can pass const int ia[3]
or const int ia[255]
, there are no differences. If we want to pass an array which size is ten, we should use reference like that:
void print10(const int (&ia)[10]) { /*...*/ }
see more discusses at http://stackoverflow.com/questions/26530659/confused-about-array-parameters
The type of elem
in the for
loop is const std::string&
.
Depends on the type of elements of initializer_list
. When the type is PODType, reference is unnecessary. Because POD
is cheap to copy(such as int
). Otherwise, Using reference(const
) is the better choice.
Error (Clang):
Non-void function 'str_subrange' should return a value. // error #1
Control may reach end of non-void function. // error #2
when you can find the preexisting object that the reference refered.
legal, it gave the values (0 ~ 9) to array ia
.
When the recursion termination condition becomes var != 0
, two situations can happen :
- case 1 : If the argument is positive, recursion stops at 0.(Note : There is one extra multiplication step though as the combined expression for factorial(5) reads 5 * 4 * 3 * 2 * 1 * 1. In terms of programming languages learning, such subtle difference probably looks quite trivial. In algorithms analysis and proof, however, this extra step may be super important.)
- case 2 : if the argument is negative, recursion would never stop. As a result, a stack overflow would occur.
the recursive function will always use val
as the parameter. a recursion loop would happen.
string (&func())[10]
// type alias
using ArrT = string[10];
ArrT& func1();
// trailing return
auto func2() -> string(&)[10];
// decltype
string arrS[10];
decltype(arrS)& func3();
I pefer the first one. because it is more simpler to me.
decltype(arrStr)& arrPtr(int i)
{
return (i % 2) ? odd : even;
}
(a) legal, repeated declarations(without definition) are legal in C++
(b) illegal, only the return type is different
(c) legal, the parameter type is different and return type is changed
(a) no error
(b) Missing default argument on parameter 'wd', 'bckgrnd'.
(a) illegal. No matching function for call to 'init'.
(b) legal, and match.
(c) legal, but not match. wd
whould be setting to '*'.
Both two should put in a header. (a) is an inline function. (b) is the declaration of useful function. we always put them in the header.
For example, the function arrPtr
in [Exercise 6.38](# Exercise-638) and make_plural
in [Exercise 6.42](# Exercise-642) should be defined as inline
. But the function func
in [Exercise 6.4](# Exercise-64) shouldn't. It is not that small and it's only being called once. Hence, it will probably not expand as inline.
Would it be possible to define
isShorter
as aconstexpr
? If so, do so. If not, explain why not.
No.
Because std::string::size()
is not a constexpr
function and s1.size() == s2.size()
is not a constant expression.
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required. (N3690 §7.1.5 [dcl.constexpr]/5)
This loop let user input a word all the way until the word is sought.
It isn't a good use of assert. because if user begin to input a word, the cin
would be always have content. so the assert
would be always true
. It is meaningless. using assert(s == sought)
is better.
candidate function:
Set of functions that are considered when resolving a function call. (all the functions with the name used in the call for which a declaration is in scope at the time of the call.)
viable function:
Subset of the candidate functions that could match a given call. It have the same number of parameters as arguments to the call, and each argument type can be converted to the corresponding parameter type.
(a) illegal. 2.56 match the double
, but 42 match the int
.
(b) match void f(int)
.
(c) match void f(int, int)
.
(d) match void f(double, double = 3.14)
.
(a) Match through a promotion
(b) Arithmetic type conversion
(a)
int calc(int&, int&); // calls lookup(int&)
int calc(const int&, const int&); // calls lookup(const int&)
(b)
int calc(char*, char*); // calls lookup(char*)
int calc(const char*, const char*); // calls lookup(const char *)
(c)
illegal. both calls lookup(char*)
int func(int a, int b);
using pFunc1 = decltype(func) *;
typedef decltype(func) *pFunc2;
using pFunc3 = int (*)(int a, int b);
using pFunc4 = int(int a, int b);
typedef int(*pFunc5)(int a, int b);
using pFunc6 = decltype(func);
std::vector<pFunc1> vec1;
std::vector<pFunc2> vec2;
std::vector<pFunc3> vec3;
std::vector<pFunc4*> vec4;
std::vector<pFunc5> vec5;
std::vector<pFunc6*> vec6;
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
vec1.push_back(add);
vec1.push_back(subtract);
vec1.push_back(multiply);
vec1.push_back(divide);
std::vector<decltype(func) *> vec{ add, subtract, multiply, divide };
for (auto f : vec)
std::cout << f(2, 2) << std::endl;
see @Mooophy 's complete codes.