##Exercise 6.1
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.
##Exercise 6.2
(a) string f() {
string s;
// ...
return s;
}
(b) void f2(int i) { /* ... */ }
(c) int calc(int v1, int v2) { /* ... */ }
(d) double square (double x) { return x * x; }
##Exercise 6.3
#include <iostream>
int fact(int val)
{
if (val == 0 || val == 1) return 1;
else return val * fact(val-1);
}
int main()
{
int j = fact(5); // j equals 120, i.e., the result of fact(5)
std::cout << "5! is " << j << std::endl;
return 0;
}
##Exercise 6.5
template <typename T>
T abs(T i)
{
return i >= 0 ? i : -i;
}
##Exercise 6.6
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?
cause the s
should not be changed by this function. but occurs
's result must be calculated by the function.
Why are these parameters references, but the char parameter
c
is not?
casue c
maybe 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
whould 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(matrix &m1, matrix &m2){ /.../ }
(b)
vector<int>::iterator change_val(int, vector<int>::iterator) { /.../ }
(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&
.
We should use const reference
as the loop control variable. because the elements in an initializer_list
are always const values, so we cannot change the value of an element in an initializer_list
.
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 preexited object that the reference refered.
legal, it gave the values (0 ~ 9) to array ia
.
##Exercise 6.33(Generics Version)
When the recursion termination condition becomes var != 0
, two situations can happen :
case 1 : If the argument is positive, recursion stops at 0. This is exactly what @shbling pointed out.
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(string (&arrStr)[10]))[10]
using ArrT = string[10];
ArrT& func1(ArrT& arr);
auto func2(ArrT& arr) -> string(&)[10];
string arrS[10];
decltype(arrS)& func3(ArrT& arr);
I pefer the first one. because it is more simpler to me.
decltype(arrStr)& arrPtr(int i)
{
return (i % 2) ? odd : even;
}
(a) illegal
(b) illegal
(c) legal
(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 and make_plural
in Exercise 6.42 should be defined as inline
. But the function func
in Exercise 6.4 shouldn't. Cause it just being call once and too many codes in the function.
Would it be possible to define
isShorter
as aconstexpr
? If so, do so. If not, explain why not.
No.
A constexpr function is defined like any other function but must meet certain restrictions: The return type and the type of each parameter in a must be a literal type
But std::string
(parameter of isShorter
) is not a literal type.
more discusses: #22
This loop let user input a word all the way until the word is sought.
It isn't a good use of assert
. The assert
macro is often used to check for conditions that “cannot happen”. But the assert
would always happen when users input EOF
directly. The behavior is very natural, so the check is meaningless. using assert(!cin || s == sought)
is more 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; }
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.