CS106L(2): Types and Structs
Core Idea
Learn C++ Basic Types: structs(including Pair + Struct), Type Deduction with Auto, Structure Binding
Lecture 2 Types and Structs - Note
View Lecture Note
Namespaces
Namespaces provide a method for preventing name conflicts in large projects.
Symbols declared inside a namespace block are placed in a named scope that prevents them from being mistaken for identically-named symbols in other scopes.
Multiple namespace blocks with the same name are allowed. All declarations within those blocks are declared in the named scope.
Example:
namespace Q {
namespace V { // V is a member of Q, and is fully defined within Q
// namespace Q::V { // C++17 alternative to the above two lines
class C { void m(); }; // C is a member of V and is fully defined within V
// C::m is only declared
void f(); // f is a member of V, but is only declared here
}
void V::f() // definition of V's member f outside of V
// f's enclosing namespaces are still the global namespace, Q, and Q::V
{
extern void h(); // This declares ::Q::V::h
}
void V::C::m() // definition of V::C::m outside of the namespace (and the class body)
// enclosing namespaces are the global namespace, Q, and Q::V
{
}
}
Lecture Note:
using namespace std;
is not a good type.
Recap: Types
C++ is a static-typed
language. Need to define the type of a var when initialising.
static-type
pros:
- better performance
- easier to understand
- better error checking
common types: int
, double
, string
, bool
, size_t
(size_t >=0)
To faster demo the examples in slide, use C++ online compiler, but it is too slow compared with command line.
Error on Lecture note page 13, // uses version (2), returns 15.0
, should be 6.0
instead.
Structs
A struct
is a group of named variables each with their own type, grouping information together when pass around
or return
.
Student DB:
struct Student {
string name; // these are called fields
string state; // separate these by semicolons
int age;
}
Student s;
s.name = "Ethan"; // use the . operator to access fields
s.state = "CA";
s.age = 20;
// Student s = {“Ethan”, “CA”, 30}; //prefer this syntax
void printStudentInfo(Student student) {
cout << student.name << " from " << student.state;
cout << " (" << student.age ")" << endl;
}
// this lookupStudent function is a bit weird.
Student lookupStudent() {
Student s;
s.name = "Ethan";
s.state = "CA";
s.age = 20;
return s;
}
Student foundStudent = lookupStudent();
cout << foundStudent.name << endl;
Pairs
A pair is a struct
with two fields.
int main() {
std::pair<bool, Student> query_result;
query_result.first = true;
Report current = query_result.second;
}
std::pair
is a template
.
Common use case: return success + result:
Student lookupStudent(string name) {
Student blank;
if (notFound(name)) return std::make_pair(false, blank);
Student result = getStudentWithName(name);
return std::make_pair(true, result);
}
std::pair<bool, Student> output = lookupStudent("Keith");
make_pair
is a generic way to make a pair without explicitly writing a type
Tuples
A tuple
is a struct with lots of fields.
int main() {
std::tuple<string, int, int> query_result;
string name = std::get<1>(query_result);
int num = std::get<2>(query_result);
}
std::tuple
<- uncommon, will use std::vector
more.
Live Code Demo: Quadratic.cpp
Have to say there are some errors in the lecture notes, here is the modified code:
#include <iostream>
#include <string>
#include <math.h>
#include <utility> // std::pair, std::make_pair
std::pair<bool, std::pair<double, double>> quadratic (int a, int b, int c) {
double inside = b*b - 4*a*c;
std::pair<double, double> blank;
if (inside < 0) return std::make_pair(false, blank);
std::pair<double, double> answer = std::make_pair((-b+sqrt(inside))/2, (-b-sqrt(inside))/2);
return std::make_pair(true, answer);
}
int main() {
int a, b, c;
std::cin >> a >> b >> c; // this gets input
std::pair<bool, std::pair<double, double>> result = quadratic(a, b, c);
if (result.first) {
std::pair<double, double> solutions = result.second;
std::cout << solutions.first << solutions.second << std::endl;
} else {
std::cout << "No solutions found!" << std::endl;
}
}
Compile:
$ g++ quadratic.cpp -std=c++2a -g -Wall -o quadratic
$ ./quadratic
POC:
Type Deduction with Auto
auto
does not mean that the variable doesn’t have a type. It means that the type is deduced by the compiler.
Usage:
auto a = 3;
auto b = 4.3;
auto c = "X";
auto d = "Hello";
auto e = std::make_pair(3, "Hello");
Wrong Usage:
auto wrong(); // this won’t work
void wrong(string a, auto b) { // neither will this work
return a * b;
}
Tips: Don’t overuse auto
. Use auto
to rescue long types, such as std::pair<bool, std::pair<double, double>>
, std::pair<double, double>
.
Structured Binding
Structured binding(Since C++17) lets you initialize directly from the contents of a struct.
It works for pair
& regular structs
, no nested structured binding.
Before:
auto p = std::make_pair("s", 5);
string a = s.first;
int b = s.second;
After:
auto p = std::make_pair("s", 5);
auto [a, b] = p;
// a is of type string
// b is of type int
// auto [a, b] = std::make_pair(...);
A better way to use quadratic
int main() {
int a, b, c;
std::cin >> a >> b >> c;
auto [found, solutions] = quadratic(a, b, c);
if (found) {
auto [x1, x2] = solutions;
std::cout << x1 << " " << x2 << endl;
} else {
std::cout << "No solutions found!" << endl;
}
}
From C++17 doc, A structured binding declaration then performs the binding in one of three possible ways, depending on E:
- Case 1: if E is an array type, then the names are bound to the array elements.
- Case 2: if E is a non-union class type and std::tuple_size is a complete type with a member named value (regardless of the type or accessibility of such member), then the “tuple-like” binding protocol is used.
- Case 3: if E is a non-union class type but std::tuple_size is not a complete type, then the names are bound to the accessible data members of E.
Haha, congrats, second lecture finished!