About C++ Programming, with ChatGPT.


Hello, if I asked you to teach me C++ from A to Z, how would you proceed?


Sure, I’d be glad to help you learn C++! Learning a programming language like C++ is a multi-step process that involves understanding the basics of the language, practicing coding, and building projects to apply your skills. Here’s a general roadmap to learn C++ from A to Z:

1. Understanding Programming Basics Before you start learning any programming language, it’s important to understand the basics of computer programming. This includes concepts like:

  • What is a program?
  • What is a programming language?
  • Different types of programming languages (Compiled vs. Interpreted)

2. Introduction to C++ Next, you’d start with the basics of C++. This would include:

  • A brief history of C++
  • The structure of a C++ program
  • Compiling and running your first C++ program

3. Basic Syntax and Data Types Here, you’d learn about the various data types in C++, how to declare variables, and the basic syntax of the language. Topics would include:

  • Variables and Data Types
  • Constants and Literals
  • Operators
  • Basic Input/Output

4. Control Structures You’d then learn about control structures in C++, which control the flow of a program. This includes:

  • Decision Making (if, if…else, switch)
  • Loops (for, while, do…while)
  • Jump statements (break, continue, goto)

5. Functions Functions are a key part of any programming language. You’d learn about:

  • Defining and calling functions
  • Function prototypes
  • Scope of variables
  • Recursion

6. Arrays and Strings Next, you’d learn about arrays and strings, which are used to store multiple values of the same type. Topics would include:

  • Declaration and initialization of arrays and strings
  • Accessing array elements
  • Multi-dimensional arrays

7. Pointers and References Pointers are a unique feature of C++ that can be difficult to understand. You’d learn about:

  • What pointers are and how to use them
  • References
  • The relationship between arrays and pointers

8. Data Structures C++ has several built-in data structures. You’d learn about:

  • Structs
  • Unions
  • Enumerations

9. Object-Oriented Programming C++ is an object-oriented programming language. You’d learn about:

  • Classes and Objects
  • Inheritance
  • Polymorphism
  • Abstraction
  • Encapsulation

10. Standard Template Library (STL) The STL is a powerful library in C++ that provides several useful data structures and algorithms. You’d learn about:

  • Containers (vectors, lists, queues, stacks, sets, maps)
  • Algorithms
  • Iterators

11. Error Handling Finally, you’d learn about error handling in C++. This would include:

  • Exception handling (try, catch, throw)
  • Creating your own exception classes

12. File I/O Interacting with files is a common task in many programs. You’d learn about:

  • Reading from and writing to files
  • File modes
  • File pointers

13. Building Projects Once you’ve learned the basics of the language, the best way to improve is by building projects. This could include:

  • Simple console-based games
  • Database applications
  • GUI applications

Throughout this process, it’s important to practice regularly. Coding is a skill that improves with practice, so try to write code every day. Also, remember to read and understand the code of others. This can provide valuable insight into different ways to solve problems.

Lastly, don’t hesitate to ask for help. Whether from a mentor, a peer, or an online community, getting help when you’re stuck is a valuable part of the learning process.

14. Advanced Topics After you’ve become comfortable with the basics and have some projects under your belt, you might want to explore some more advanced topics:

  • Multi-threading and concurrency: Understand how to write code that can perform multiple tasks at the same time.
  • Memory management: Deepen your understanding of dynamic memory and how to manage it properly.
  • Templates: Learn how to write code that is type-agnostic and can work with different data types.
  • Networking: Learn about sockets and how to write programs that communicate over the internet.
  • Design patterns: These are tried and tested solutions to common problems in software design, and learning them can make you a better programmer.

15. Reading Documentation and Understanding Libraries Learning to read and understand documentation is a crucial skill for any programmer. It enables you to learn and use libraries and frameworks that can greatly increase your productivity.

16. Debugging and Testing Learn about various techniques for debugging your code, and understand how to write tests to ensure your code is working as expected. This includes unit testing, integration testing, and regression testing.

17. Code Reviews Participating in code reviews, both as the reviewer and reviewee, is a great way to learn from others and improve your own coding skills.

18. Contributing to Open Source Once you’re comfortable with C++, contributing to open source projects can be a great way to improve your skills and contribute to the community. You’ll learn to collaborate with others, use version control systems, and work on large codebases.

19. Continuous Learning Programming is a field that’s constantly evolving, and the most successful programmers are those who continue to learn throughout their career. Keep up with new C++ standards, explore other programming languages, and never stop learning.

Remember, learning to code is a marathon, not a sprint. It’s okay to feel overwhelmed at times, and it’s normal to not understand everything immediately. Just keep at it, and with time and practice, you’ll get there.

Would you like to start with any specific topic or have any questions about the roadmap?


You know, I think discussion of assembly is necessary since it is an integral part of C++ concept, and programming in general, it explains mechanics or possible ones given syntactical expression of C++ in particular.


You’re absolutely right, a basic understanding of assembly language can be very beneficial when learning C++. It’s not necessary for beginners, but as you start working with more complex code, understanding assembly can help you write more efficient code and debug more effectively.

Assembly language is a low-level programming language that closely corresponds to the machine code instructions of a specific CPU architecture. This is in contrast to high-level languages like C++, which are designed to be portable across multiple systems.

Here are some ways in which understanding assembly language can be beneficial when programming in C++:

1. Performance Tuning Understanding assembly can help you write more efficient C++ code. For example, you might see how different C++ constructs compile to different assembly instructions and use this knowledge to choose the most efficient construct.

2. Debugging Sometimes, a debugger may drop down to the assembly level, especially when you’re stepping through code at a low level. Understanding assembly can make it easier to understand what’s going on in these situations.

3. Security Many security exploits involve low-level code, and understanding assembly can help you write more secure code.

4. Understanding Computers Assembly language is much closer to the hardware than C++. Learning it can give you a better understanding of how computers work, which can make you a better programmer.

If you’re interested in learning assembly as part of your C++ education, here’s a simple roadmap:

1. Understanding the Basics Learn about the basic components of assembly language, like registers, memory, instructions, and addressing modes.

2. Simple Programs Start by writing simple assembly programs, like adding two numbers or sorting an array. This will give you a feel for the language.

3. Interfacing with C++ Learn how to call assembly code from C++ and vice versa. This will allow you to use assembly where it’s beneficial while still taking advantage of the power and convenience of C++.

4. Complex Programs As you get more comfortable with assembly, start writing more complex programs. This will help solidify your understanding and give you practice writing assembly.

5. Reading Compiler Output A good way to learn assembly is by looking at the assembly code generated by a C++ compiler. This can give you insight into how different C++ constructs are implemented at the machine level.

6. Optimization Finally, learn about optimization techniques in assembly. This can help you write faster, more efficient code.

Remember, assembly language is complex and difficult to read, so it’s not necessary for most C++ programming. However, if you’re interested in low-level programming or want to understand computers at a deeper level, learning assembly can be very beneficial.


And also directly compare a pairs of code, one in C++, to one or more in potential assembly. For instance, a brief program in assembly that explains the steps to perform a line of some mathematical or logical expression in C++. You did not specifically mention the property of short circuiting which is a necessary logical concept in C++, and this can be explained simply in assembly.


Certainly, understanding how high-level language constructs translate to assembly can give you a greater understanding of how your code is executed by the machine. It’s worth noting that the exact assembly code generated can vary depending on the compiler and optimization settings used, but we can look at a general example.

Let’s take a simple C++ logical expression that uses short-circuit evaluation:

In this C++ code, we’re using a logical AND (&&) operation, which performs short-circuit evaluation. This means if the first operand is false, the second operand is not evaluated at all. This is important in this case because if b is zero, a / b would cause a division by zero error. However, due to short-circuit evaluation, this operation is never performed if b is zero.

int a = 10;
int b = 0;
if (b != 0 && a / b > 2) {
    // Do something

Now, let’s see how this could be translated into assembly. This will be a somewhat simplified and not necessarily exact representation, as actual generated assembly can vary greatly:

mov eax, [b]      ; Load the value of b into the EAX register
test eax, eax     ; Test if EAX (b) is zero
jz end            ; If it is, jump to the end (short circuit)
mov ebx, [a]      ; Load the value of a into the EBX register
div ebx, eax      ; Divide a by b
cmp eax, 2        ; Compare the result to 2
jle end           ; If the result is less than or equal to 2, jump to the end
; Here you would add the assembly code for the "Do something" part
; Rest of the code

This assembly code does the same thing as the C++ code. First, it checks if b is zero, and if it is, it jumps to the end, effectively skipping the rest of the code block. This is the assembly equivalent of the short-circuit behavior in the C++ code. If b is not zero, it goes on to perform the division and comparison.

Again, this is a simplified example. The actual assembly generated by a compiler would likely look quite different and be more complex, particularly for more complex C++ code. However, it demonstrates the basic concept of how a high-level construct like a short-circuiting logical operation can be implemented at the assembly level.


Specifically, the left-to-right reading order of the C++ syntax is not necessarily execution order, and any particular part of function may avoid execution altogether. The compiler causes reordering which is why when writing C++, it can be useful to express language in a way that demystifies somehow, in C++, what the compiler could be doing and hopefully is.


Indeed, the order of execution in a high-level language like C++ doesn’t necessarily correspond directly to the order of operations at the assembly level. Compilers are free to rearrange the code as long as they maintain the “as-if” rule, which states that the observable behavior of the optimized code must be the same as if it were executed exactly as written.

Understanding this can indeed be beneficial when writing C++. As you write more complex code, you can start to take advantage of this to write more efficient code. For example, knowing that certain operations can be expensive in terms of CPU time, you can structure your code to avoid them when possible.

Consider the following example:

if (expensiveComputation() || simpleCheck()) {
    // Do something

In this case, expensiveComputation() is a function that takes a long time to compute, while simpleCheck() is a simple operation. Because C++ uses short-circuit evaluation for the || operator, simpleCheck() won’t be executed if expensiveComputation() returns true.

However, if you know that simpleCheck() is likely to be true most of the time, you could rewrite the code as follows to make it more efficient:

if (simpleCheck() || expensiveComputation()) {
    // Do something

Now, expensiveComputation() won’t be executed if simpleCheck() returns true, potentially saving a lot of CPU time.

This is just one example of how understanding the behavior of the compiler can help you write more efficient code. Other techniques might involve understanding how the compiler optimizes loop structures, function calls, and other high-level constructs.

In general, while it’s not necessary to always be thinking about how your C++ code will be translated to assembly, having a basic understanding of the process can certainly make you a better programmer. It can help you understand why certain code constructs are more efficient than others, and it can help you debug and optimize your code more effectively.


A single “if” statement, being a C++ block of code, in assembly is in fact broken down into additional if-equivalent concepts, some of which may or may not be executed in their apparent sequence.


Yes, you’re correct. The structure of an if statement in C++ does not correspond directly to the sequential flow of execution in assembly language. This is because assembly language lacks the high-level control structures of C++, and instead uses jumps (branching) to control the flow of execution.

Consider a simple if statement in C++:

int a = 5;
int b = 10;
if (a < b) {
    a = b;

The equivalent assembly might look something like this:

mov eax, [a]      ; Load the value of a into the EAX register
cmp eax, [b]      ; Compare the value of a to b
jge end_if        ; If a is greater than or equal to b, jump to the end of the if block
mov eax, [b]      ; Otherwise, set a to the value of b
mov [a], eax
; Rest of the code

In the assembly code, instead of a clear if block, we have a comparison followed by a conditional jump. If the condition is false, the jump is taken, and the code inside the if block is skipped. If the condition is true, the jump is not taken, and the code inside the if block is executed.

Note that this is a simple example, and real-world compiler-generated assembly would likely be more complex, especially with optimizations enabled.

Also, because of the nature of assembly language, the exact order of the instructions in the assembly code does not necessarily match the order of the code in the C++ program. The compiler can rearrange the instructions for efficiency as long as it maintains the same overall effect (the “as-if” rule).

This is a key point in understanding how high-level constructs in C++ translate into low-level assembly language, and can be helpful in writing more efficient code and debugging at a low level.


And then, while understanding Assembly is essential for good programming practices, understanding the STL is equally important, if not more.


Absolutely, the Standard Template Library (STL) in C++ is an incredibly powerful tool that can greatly increase your productivity and the efficiency of your code. It provides a set of common classes and functions, which includes, but is not limited to, collections like vectors, lists, and maps, as well as algorithms that allow you to perform operations like sorting, searching, and manipulating these collections.

Here’s a brief overview of the main components of the STL and why they’re important:

1. Containers STL containers are template classes that store data. They manage the storage space that they use, expanding and contracting as needed. STL Containers are like C++ built-in types, they can be created, initialized, copied, and assigned, passed as parameters, and returned as results. Most containers have similar member functions and use similar naming conventions.

2. Algorithms STL algorithms act on containers. They provide the means by which you will manipulate the contents of containers. For example, you might sort the elements, search for specific items or count certain elements.

3. Iterators Iterators are used to step through the elements of collections of things. They are primarily used with sequence containers, though certain associative containers also support them.

4. Function Objects Function objects are objects that can act as functions. This is possible because of operator overloading.

5. Allocators Allocators are used to customize the allocation and deallocation of memory.

Understanding how to use these tools can greatly simplify your code and make it more efficient. For example, instead of manually allocating an array and keeping track of its size, you can simply use a vector that automatically manages its own size.

Furthermore, the STL is designed with efficiency in mind, and its algorithms are generally as fast or faster than equivalent hand-written code. For example, the sort function provided by the STL is typically much faster than a simple sort function you might write yourself.

In short, the STL is a crucial tool for any C++ programmer. Not only does it simplify your code and make it more efficient, but understanding it also gives you a deeper understanding of the C++ language itself. It’s certainly worth spending the time to become comfortable with the STL if you’re serious about programming in C++.


In fact, using the stl is going to increase the capabilities of average experience programmers, and given that STL is constantly advancing with more advanced C++ concepts, like views, these STL concepts are probably expressing a more advanced programming form than older STL and C++ concepts.


Absolutely. The STL has evolved significantly since its inception, and it continues to incorporate more advanced features and concepts with each new revision of the C++ standard.

One of the goals of the STL and C++ standard committee is to make complex tasks simpler and more intuitive for developers. As a result, modern STL includes features that allow developers to write more concise, expressive, and efficient code.

Let’s take a look at a few modern STL features:

1. Smart Pointers (C++11 and onwards): Traditional raw pointers in C++ are powerful but can easily lead to memory leaks and other issues. Smart pointers, like shared_ptr, unique_ptr, and weak_ptr, help manage the lifecycle of objects in memory, making it easier to write safe and efficient code.

2. Lambda Expressions (C++11 and onwards): Lambda expressions allow you to write inline anonymous functions that can be used in algorithms. This can make your code more concise and expressive.

3. Range-based for loops (C++11 and onwards): These provide a simpler, more readable syntax for iterating over elements in a container.

4. The auto Keyword (C++11 and onwards): This keyword allows the compiler to determine the appropriate type for a variable, which can simplify your code and reduce errors.

5. Concepts (C++20): Concepts are a way to express the intent of a template. They are used to specify the requirements on template arguments, making it easier to understand how to use them.

6. Ranges (C++20): The ranges library is a new way to work with sequences of data in a more expressive and intuitive way. It introduces the concept of “views” which are lightweight, lazy, composable abstractions on top of data.

By using these modern features of the STL, developers can write code that’s more expressive, more efficient, and less prone to errors. Furthermore, as the STL evolves, it often incorporates techniques and patterns that have been proven to be effective in real-world applications, so using the STL can also help developers align their code with best practices.

It’s also worth noting that many modern C++ guidelines, such as the Core Guidelines, encourage the use of these modern STL features. So learning and using them can also be an important part of writing “modern” C++ code.


Understanding these new C++ concepts parts C++ program expression far from C and assembly.


Indeed, modern C++ introduces a variety of high-level constructs and abstractions that make it a significantly different language from C and make its code look quite dissimilar to assembly. While C++ is a superset of C and retains a lot of its low-level capabilities, it also provides a lot of powerful features that allow for much higher-level programming.

Features like classes, templates, exceptions, and STL containers and algorithms can make C++ code look very different from C code. For example, a simple task like sorting an array of integers can be done in one line in C++ using the STL sort algorithm, whereas in C it might require implementing a sort algorithm manually.

With the introduction of modern features like smart pointers, lambda functions, the auto keyword, range-based for loops, concepts, and ranges, C++ code can become even more high-level and abstract, often looking quite different from traditional C-style code.

For instance, consider this piece of modern C++ code:

std::vector<int> numbers = {5, 4, 3, 1, 2};
std::sort(numbers.begin(), numbers.end());
auto result = std::find_if(numbers.begin(), numbers.end(), [](int n) { return n > 3; });
if (result != numbers.end())
    std::cout << "Found a number greater than 3: " << *result << "\n";

This code, which sorts a vector of numbers and then uses a lambda function to find the first number greater than 3, would look quite different in both C and assembly. In fact, some of these features, like lambda functions and the auto keyword, don’t have direct equivalents in C or assembly.

So yes, while understanding assembly can provide a solid foundation for understanding how computers work and how your C++ code is executed, it’s also important to recognize that modern C++ provides many powerful abstractions that allow you to write code at a much higher level, often making it look quite different from the equivalent assembly code.


One of the great things about the STL, is that one of its concepts is templated style programming, where code A is basically inserted into or adapted to code B in some way, and then some common operation is performed automagically for you. You pass inline lambdas to some calling function in the STL, or using functors of some sort. Functors and lambdas are similar in use-case and basically the same thing. The concept that more and more often, some segment of code is in-fact some object which is passed around for common operations, which you can probably find in part in the STL. In this concept, a functor or lambda, can potentially have a lifetime connected with some variables. The functor in C++ is an object which implements the bubbles or execution operator, (), or another operator, such as comparisons like > or <=>. This operator can be overloaded and accept various arguments. Groups of different code can call a given operator identically, and on fundamentally different objects. This property is exploited through template programming in which some code A is in-fact compiled differently based on the templated property, B. The function, however, is some constant useful function found often in the STL.


You’ve given a great description of the power and flexibility of template programming in C++, particularly when combined with functors and lambdas.

As you’ve mentioned, a functor is an object that can be called as if it were an ordinary function. This is done by defining an operator() for the object. Functors are often used with STL algorithms that require a function, such as std::sort, std::transform, std::for_each, and many others.

Here is a simple example of a functor that can be used to increment the values in a container:

struct Increment
    int operator()(int x) const { return x + 1; }

std::vector<int> values = {1, 2, 3, 4, 5};
std::transform(values.begin(), values.end(), values.begin(), Increment());

In this code, the Increment functor is used with the std::transform algorithm to add 1 to each value in the values vector.

Lambdas, introduced in C++11, provide a more convenient and flexible way to create simple functors. A lambda expression can be used to create an unnamed function object right at the location where you use it.

Using a lambda, the above example could be written like this:

std::vector<int> values = {1, 2, 3, 4, 5};
std::transform(values.begin(), values.end(), values.begin(), [](int x) { return x + 1; });

In this code, the lambda function [](int x) { return x + 1; } does exactly the same thing as the Increment functor in the previous example.

Templates in C++ provide a way to write generic code that works with different data types. STL algorithms make extensive use of templates, allowing them to work with any container and any type of data. The combination of templates, functors, and lambdas is incredibly powerful, allowing you to write highly reusable and flexible code in C++.

These features, in a way, make C++ a multi-paradigm language, supporting not just procedural and object-oriented programming, but also generic programming, and to some extent, functional programming. They make C++ a highly expressive language, capable of handling a wide range of programming tasks efficiently and elegantly.


STL also provides additional programming concepts such as parallel programming, via algorithms in the STL. Any algorithm you use, if it is in the STL, you can easily cause to run in a parallel way. For instance, you could execute std::sort with a parallel algorithm.


Yes, as of C++17, the STL includes support for parallel algorithms. This means that many of the existing algorithms in the <algorithm> library now have overloads that accept an additional first argument, which is an execution policy. This execution policy instructs the algorithm how to execute: sequentially (the default), in parallel, or in parallel with vectorization.

Here’s an example of how you might use a parallel algorithm:

#include <vector>
#include <algorithm>
#include <execution>

std::vector<int> numbers = {5, 4, 3, 1, 2};

std::sort(std::execution::par, numbers.begin(), numbers.end());

In this code, std::execution::par is an execution policy that instructs std::sort to execute in parallel. The C++ runtime will automatically manage the creation and synchronization of threads to perform the sort operation.

The execution policies available are:

  • std::execution::seq: Specifies that the algorithm should execute sequentially.
  • std::execution::par: Specifies that the algorithm may be parallelized.
  • std::execution::par_unseq: Specifies that the algorithm may be parallelized and vectorized.

Remember that not all tasks will see a performance improvement from parallel execution. Sometimes the overhead of creating and managing threads can outweigh the benefits of parallelism, especially for small tasks or data sets.

Additionally, not all STL algorithms support parallel execution, and not all systems or compilers may support the parallel STL. So, while the parallel STL can be a powerful tool for improving performance, it’s important to understand its limitations and to profile your code to ensure you’re actually seeing a performance improvement.


It seems apparent that some aspects of the hardware are going to affect performance of parallel code, and in fact could have a faster single threaded execution, given an algorithm in C++. Generally, there are parallel programming best practices which can be understood. One practice is to understand that computer performance has a lot to do with the cpu’s cache, and its behavior during execution. In some cases, you can write code that considers this behavior to some extent, and potentially, have drastic gains between examples that do not. The more threads that are introduced to the same dataset, the more carefully the cache would need to be understood and managed to maintain highest efficiency.


Absolutely. Understanding the hardware on which your code runs is critical to achieving the best performance, especially when it comes to parallelism. The CPU cache plays a significant role in this regard. The cache is much faster than main memory (RAM), and how you access data can have a huge impact on performance due to the way caching works.

Here are some things to consider:

1. Temporal locality: If a particular memory location is accessed, it’s likely to be accessed again in the near future. Therefore, once data is fetched into the cache, it’s beneficial to use it as much as possible before it gets replaced.

2. Spatial locality: Memory locations that are close to each other tend to be accessed close together in time. When a memory location is fetched into the cache, nearby data is also fetched. Therefore, accessing memory in a linear fashion is generally more cache-efficient than randomly jumping around memory.

3. False sharing: This is a situation that can occur in multi-threaded programs where threads invalidate each other’s cached data. Even though each thread might be working with a different variable, if those variables are located on the same cache line, then writing to them from different threads can cause the cache line to constantly be invalidated and refreshed, severely hurting performance.

4. Granularity of parallelism: If the work done in each parallel task is too small, then the overhead of starting, synchronizing, and stopping threads can outweigh the benefits of parallelism. On the other hand, if tasks are too large, then load imbalance can become a problem, where some threads finish their work and sit idle while others are still busy.

A good understanding of these concepts can guide you in designing your algorithms and data structures for efficient parallel execution. However, it’s also important to profile and test your code on the actual hardware where it will run, as the specifics can vary greatly between different systems. And remember, the most efficient algorithm is often the simplest one that gets the job done, so don’t prematurely optimize before you have a working solution.


That leads to programming paradigm shift which is somehow considerate of all of these things, and this is a new C++ paradigm.


Yes, modern C++ has indeed seen a paradigm shift towards more high-level, abstract, and safer programming styles. Some of this is facilitated by new language features and library components introduced in the recent standards, which help in writing code that is more performant, more maintainable, and less prone to common bugs and pitfalls.

For example, the introduction of smart pointers has made it much easier to manage memory safely, reducing the risk of memory leaks and dangling pointers. Lambda expressions and auto type deduction have made code more concise and expressive. The range-based for loop simplifies the process of iterating over containers. Concepts and constraints make templates easier to use and understand. The parallel algorithms in the Standard Library offer a simple and efficient way to parallelize code.

All of these features encourage a style of programming that emphasizes simplicity, safety, and efficiency. They enable developers to focus on the high-level logic of their programs, rather than getting bogged down in low-level details and error-prone manual memory management.

This doesn’t mean that understanding the underlying hardware and low-level programming concepts isn’t important. On the contrary, to get the most out of these high-level features, it’s crucial to have a good understanding of what’s happening under the hood. This includes understanding how memory works, how your code is compiled and executed, and how to profile and optimize your code.

In summary, modern C++ promotes a paradigm where the language and its libraries provide you with powerful tools and abstractions, but also give you the flexibility to drop down to a lower level when necessary. It’s a balance between high-level simplicity and low-level control, making it a very powerful and versatile language for a wide range of programming tasks.


That said, true to this concept, there are many use-cases where considering various levels of manual memory management is beneficial. This capacity is expressed through the C++ concept of views which turned out to be, for the most part, std::span.


Yes, you’re correct. std::span is a feature introduced in C++20 that represents a lightweight, non-owning reference to a contiguous sequence, or a “view” of data. It can be thought of as a pointer, length pair, and it provides a way to pass around references to memory without copying or transferring ownership.

One advantage of std::span and other view types is that they offer a level of abstraction over raw data while being very lightweight and not incurring the overhead of owning containers. They provide a way to operate on data sequences in a generic way without needing to know the details of the underlying container.

Here’s a simple example:

void print_elements(std::span<int> elements)
    for (int e : elements) {
        std::cout << e << '\n';

std::vector<int> v = {1, 2, 3, 4, 5};
print_elements(v);  // You can pass a std::vector

int arr[] = {6, 7, 8, 9, 10};
print_elements(arr);  // You can also pass a C-style array

In this example, the print_elements function can accept any contiguous sequence of integers, without needing to know or care whether it’s a std::vector, a C-style array, or some other kind of sequence.

However, it’s important to understand that std::span and similar view types don’t own the data they refer to. They’re just views into other data. This means that you need to be careful to ensure that the underlying data outlives the view. If the data a view refers to is destroyed, then the view is left dangling, much like a dangling pointer.

std::span is just one of several new view types in modern C++. Others include std::string_view and various range views in the Ranges library, which provide powerful and efficient ways to manipulate and view data sequences.


In fact, the implementation of a span is the concept of a pointer, and a length. Pointers are here – but abstracted away into an object. It’s a pointer at heart, but never necessarily sees a new or delete call on it. This concept (raw pointer) is still in C++ at its core, and not all pointers used in a program should be some “std pointer type”. Pointers and References and Iterators are everywhere in C++, and essential to good programming.


Yes, you’re absolutely right. While modern C++ has introduced abstractions like smart pointers (std::unique_ptr, std::shared_ptr) and container classes that handle memory management, the core concepts of pointers, references, and iterators are still fundamentally important to understanding and effectively using the language.

C++ is a language that allows for both high-level abstractions and low-level direct manipulation of resources. This is a powerful but potentially dangerous combination, as it gives programmers a lot of flexibility but also a lot of rope to hang themselves with, so to speak.

std::span is a good example of this duality. It provides an easy-to-use, high-level interface for working with contiguous sequences of data, but it’s essentially just a wrapper around a raw pointer and a size. As such, it doesn’t manage the memory it points to, and it’s up to the programmer to ensure that the std::span doesn’t outlive the data it refers to.

Iterators are another fundamental concept in C++. They provide a way to access elements in a container, and they abstract away the details of the underlying container. You can think of them as a kind of generalization of pointers. However, like pointers, they don’t own the elements they point to, and they can be invalidated by operations on the container.

In conclusion, while modern C++ provides many tools and abstractions to make memory management safer and easier, understanding the underlying concepts of pointers, references, and iterators is still crucial. It’s this combination of high-level abstractions and low-level control that gives C++ its power and flexibility.


In programming practices, References to objects can be used to make code clearer or faster, and these are pointers which should not see a new or delete called on them, either. However, they are really the same thing. Any code, whatever its source, can be like:

auto& x = value;

In some cases this will speed up code that is often using dereferencing, such as


multiple times, becomes instead uses of plain e:

auto& e = x.y.z.e;

Furthermore, you can change the name of any variable this way.

 auto& x = someVariable;

Now both of these variable names refer to the same object.


Absolutely. The use of references in C++ can lead to cleaner, more understandable code, and in some cases, better performance. A reference is essentially a constant pointer that always points to the referred object and cannot be null or reseated to refer to a different object. They are easier to use and less prone to errors than raw pointers, which makes them a preferred choice in many scenarios.

Let’s consider the example you’ve given:

auto& x = someVariable;

In this code, x is a reference to someVariable. Any changes made to x will actually change someVariable, because they both refer to the same object. This is a very powerful concept, especially when passing arguments to functions or returning values from functions.

Consider a function that takes a large object as an argument. If you pass the argument by value, a new copy of the object is created, which can be expensive in terms of memory and performance. However, if you pass the argument by reference, no new copy is created, and the function works directly on the original object:

void modifyObject(Object& obj) {
    // Modify obj directly...

In this code, modifyObject takes a reference to an Object. Any changes it makes to obj will be made on the original object that was passed in. This avoids the need to make a copy of the object, which can significantly improve performance when working with large objects.

The use of const with references can further increase safety by preventing the referred object from being modified:

void printObject(const Object& obj) {
    // Print obj, but cannot modify it...

In this code, printObject takes a const reference to an Object. This means it can read from obj, but it cannot modify it.

Using references in this way can make your code clearer, safer, and more efficient. However, like all powerful tools, they should be used judiciously and with an understanding of their implications.


One thing const references are useful for is setting a default argument parameter of a function to something sensical.


Yes, using const references as default parameters in functions can be very useful. It allows you to provide default values for complex types without the need for making a copy, which can be costly in terms of performance for large objects.

Here’s an example:

class MyClass {
    void doSomething(const std::string& str = "default value") {
        // Do something with str...

In this example, doSomething has a const std::string& parameter str with a default value of "default value". When this function is called without arguments, it uses this default value.

The use of a const reference here avoids making a copy of the string, which would be the case if the parameter was just std::string. Also, as str is a const reference, the function can’t modify the passed argument, which is a good practice for function parameters that aren’t meant to be modified.

However, one thing to be aware of when using default arguments like this is the lifetime of the default value. In this case, “default value” is a string literal which has static storage duration, meaning it lives for the entire duration of the program. If the default argument was a reference to an object with automatic storage duration (for example, a local variable in the calling function), you could end up with a dangling reference if that object gets destroyed before the function call.


C++ is a language that coexists in a C++ library ecosystem and opensource projects, and indeed writing libraries of your own code for access in other projects is crucial.


Absolutely! Writing and using libraries is a critical part of software development, and this is especially true in C++. The Standard Library that comes with C++ is a powerful tool, but it’s just the beginning. There are countless other libraries available that can help you do everything from parsing JSON, to rendering 3D graphics, to making HTTP requests, to implementing machine learning models, and much more.

In addition, writing your own libraries can help you organize your code, make it more reusable, and simplify your build processes. Here are some steps to create your own library in C++:

  1. Write the Library Code: This is your header files (.h or .hpp) and source files (.cpp). The header files declare your classes, functions, etc., and the source files define them.
  2. Compile the Library: You compile your library code into either a static library (.lib or .a) or a dynamic library (.dll or .so), depending on your needs. Static libraries are included in the executable at compile time, while dynamic libraries are loaded at runtime.
  3. Use the Library: In the programs where you want to use your library, you #include your library’s header files and link to the library file when you compile the program.
  4. Distribute the Library: If you want others to be able to use your library, you’ll need to distribute the library file and the header files. You may also want to provide documentation, examples, build scripts, etc.

When writing a library, some best practices include:

  • Use an intuitive namespace and organize your code logically.
  • Avoid global variables as they can cause conflicts in the programs that use your library.
  • Make sure to document your code well, so others can understand how to use it.
  • Use versioning to track changes and maintain compatibility.

Open-source libraries can be a great resource for learning and inspiration. You can learn a lot about design patterns, best practices, and advanced language features by reading and understanding their code. Contributing to open-source libraries can also be a good way to improve your skills and make connections in the developer community.

In conclusion, libraries, whether they’re third-party or your own, are a fundamental part of C++ development, and learning how to use and create them is a valuable skill for any C++ programmer.


Understanding source revisioning and history is important, and tools such as github are crucial to good practices.


You’re absolutely right. Version control systems, such as Git, and platforms like GitHub, Bitbucket, and GitLab, are essential tools for any modern software development process, including C++ development.

Here are some reasons why understanding and using these tools is so important:

  1. Collaboration: Multiple developers can work on the same codebase without overwriting each other’s changes. They can work on different features or bugs in isolated environments (branches) and then merge their changes back into the main codebase when they’re ready.
  2. Versioning: Every commit serves as a checkpoint that you can return to at any time. This makes it easy to track changes over time, find when a particular change was made, and even roll back changes if necessary.
  3. Backup and Synchronization: Your code is stored remotely on a server like GitHub. This not only serves as a backup, but it also makes it easy to synchronize your work across different machines.
  4. Code Review: Platforms like GitHub provide tools for code review. Other developers can review your changes, suggest improvements, and catch bugs before they make it into the main codebase.
  5. Continuous Integration/Continuous Deployment (CI/CD): These platforms often provide or integrate with tools that can automatically build, test, and deploy your code whenever you make a commit.
  6. Open Source Contribution: If you’re working on open source projects, understanding these tools is essential. Most open source projects use Git for version control and host their code on platforms like GitHub.

If you’re just starting with Git, here are some basic concepts you should understand:

  • Repository: This is your project’s directory that Git tracks. It includes all your project files and a .git directory that stores your history and settings.
  • Commit: This is a set of changes that you save (commit) to your repository. Each commit has a unique ID and includes metadata like who made the commit, when it was made, and a message describing the changes.
  • Branch: This is a parallel version of your code that you can modify without affecting other branches. The main branch is often called master or main.
  • Merge: This is the process of combining changes from one branch into another.
  • Pull/Push: These are the operations to get changes from (pull) and send changes to (push) a remote repository.

By learning these tools and concepts, you’ll be well equipped to collaborate on software projects, maintain your code’s history, review and improve code quality, and contribute to open source projects.


C++ is an ongoing technology process which meets new standards and capabilities more and more often. It is something that changes and potentially in dramatic ways. Further study seems necessary and constant, considering the ecosystem of technology is already vast, and ever advancing in different ways. C++ can be used to build any sort of application rapidly. From GUI to graphics, ML to games, VR, anything else, C++ is there somewhere, or could be.


You’re absolutely correct. C++ is a versatile and powerful language that is constantly evolving to meet the needs of modern software development. It’s used in a wide range of applications, from system software and embedded systems, to video games, web servers, desktop applications, machine learning, and even in the development of other programming languages.

The C++ language itself has been undergoing regular updates, with new versions released every few years (C++11, C++14, C++17, C++20, and so on). These updates introduce new features and enhancements that make the language more powerful, easier to use, and better suited to modern programming paradigms. Some of the recent additions include smart pointers for better memory management, lambda functions for easier functional programming, and improvements to concurrency and parallelism.

To stay current with these changes, continuous learning is indeed necessary. Here are some ways to keep up with the evolving C++ landscape:

  1. Follow the Standards Committee: The ISO C++ committee is responsible for the development of the C++ language. They regularly publish documents detailing proposed changes to the language. Following these publications can give you a glimpse into the future of C++.
  2. Read Books and Online Resources: There are many great books and online resources for learning C++, from beginner guides to in-depth texts on specific aspects of the language. Websites like cppreference.com provide up-to-date references on C++ language features and the standard library.
  3. Online Courses and Tutorials: Websites like Coursera, Udemy, and Pluralsight offer courses on C++ that are updated regularly to incorporate new language features.
  4. Conferences and Meetups: Events like CppCon, Meeting C++, and local C++ meetups are great places to learn about the latest developments in C++, network with other developers, and even contribute to the language’s development.
  5. Practice: The best way to learn is by doing. Regularly writing C++ code, experimenting with new features, and working on projects will help you understand the practical implications of changes to the language.
  6. Join Online Communities: Online forums like Stack Overflow, the C++ subreddit, and C++ forums are places where you can ask questions, share your knowledge, and learn from other developers’ experiences.

In conclusion, while C++ is a mature language, it continues to evolve and adapt to the changing technology landscape. By continuously learning and adapting, you can leverage the power of C++ to build a wide range of applications.


It is necessary to remember that this evolution can at any point be dramatic. In ten years time, the language could appear different and have different, more advanced, capabilities and concept implementations, such that it could be basically unrecognizable. Updating C++ learning and teaching practices must also frequently evolve or be rendered depreciated or plain wrong.


Absolutely, the pace of technological advancement means that languages like C++ are continuously evolving, sometimes in ways that significantly change how they are used. New features, paradigms, and best practices are introduced that can make old ways of doing things obsolete or even incorrect.

As you rightly pointed out, this necessitates a commitment to lifelong learning and adaptability from developers. Regularly refreshing one’s knowledge, keeping up-to-date with the latest standards and practices, and continually challenging and updating one’s understanding are all key to staying relevant in the field of software development.

For educators, this poses the additional challenge of ensuring that their teaching materials and methods are up-to-date. This includes not just the content (e.g., the features of the language being taught) but also the methodologies and tools used for teaching (e.g., cloud-based IDEs, collaborative coding tools, etc.).

Education and industry need to work together to ensure that the skills being taught are relevant to the current and future needs of the industry. Regularly updating curricula, involving industry professionals in education, and promoting a culture of continuous learning can all help in achieving this.

It’s an exciting time to be in the field of software development. The rapid pace of change can be challenging, but it also opens up new opportunities for learning and growth. Embracing this change and the opportunities it brings is key to a successful career in software development.


Lets talk about writing ML in C++.


Machine Learning (ML) in C++ can be a bit more challenging compared to languages specifically designed for data science tasks, like Python or R. However, there are several libraries available in C++ for machine learning, and C++ has the advantage of performance and control over low-level aspects of the computation, which can be important in some ML applications.

Here are some popular C++ libraries for machine learning:

  1. MLPack: MLPack is a scalable machine learning library, written in C++, that aims to provide fast, extensible implementations of cutting-edge machine learning algorithms. MLPack provides these algorithms as simple command-line programs and C++ classes which can then be integrated into larger-scale machine learning solutions.
  2. Dlib: Dlib is a modern C++ toolkit containing machine learning algorithms and tools for creating complex software in C++ to solve real world problems. It is used in both industry and academia in a wide range of domains including robotics, embedded devices, mobile phones, and large high performance computing environments.
  3. Shark: Shark is a fast, modular, feature-rich open-source C++ machine learning library. It provides methods for linear and nonlinear optimization, kernel-based learning algorithms, neural networks, and various other machine learning techniques.
  4. TinyDNN: TinyDNN is a C++14 implementation of deep learning. It is suitable for deep learning on limited computational resource, embedded systems, and IoT devices.

When working with machine learning in C++, remember that data manipulation and analysis might not be as straightforward as in Python. You may need additional libraries for tasks like reading CSV files, manipulating data frames, and visualizing data. Libraries such as Eigen for linear algebra, Boost for data structures and processing, and Gnuplot++ for plotting can be helpful.

Even though Python dominates the ML field, there are certain cases where using C++ for ML makes sense:

  • Performance-sensitive applications: C++ is typically much faster than Python, which can make a big difference when processing large datasets or when the ML application needs to be highly responsive, such as in real-time trading systems.
  • Existing C++ codebases: If you are working in a codebase that is already in C++, it might make sense to use C++ for ML as well, rather than introducing a new language into the mix.
  • Embedded systems: C++ is commonly used for programming embedded systems, which often have strict constraints on memory and computational resources.

Remember, the choice of programming language often depends on the problem you’re trying to solve, the requirements of the system you’re working on, and the tools you’re most comfortable with.


Specifically, to how can I implement ML from scratch in a basic main example, in modern C++.


Implementing machine learning algorithms from scratch is a great way to learn the underlying concepts and mechanics. Let’s create a simple linear regression model from scratch using modern C++. Note that we’re not going to use any ML libraries here, the focus is on understanding the principles.

A linear regression model tries to fit a line to data points that minimizes the sum of the squares of the distances from each data point to the line. The line is defined by the equation y = mx + b, where m is the slope and b is the y-intercept. We’ll use gradient descent to iteratively adjust m and b to minimize the cost function (mean squared error).

Here is a simple example:

#include <iostream>
#include <vector>
#include <cmath>

// Function to compute the mean of a vector
double mean(const std::vector<double>& v) {
    double sum = 0.0;
    for (double x : v) {
        sum += x;
    return sum / v.size();

// Function to compute error
double compute_error(const std::vector<double>& x,
                     const std::vector<double>& y,
                     double m, double b) {
    double totalError = 0.0;
    for (size_t i = 0; i < x.size(); ++i) {
        totalError += std::pow(y[i] - (m * x[i] + b), 2);
    return totalError / x.size();

// Function to perform gradient descent
void gradient_descent(std::vector<double>& x,
                      std::vector<double>& y,
                      double& m, double& b,
                      double learning_rate, int num_iterations) {
    size_t N = x.size();
    for (int i = 0; i < num_iterations; ++i) {
        double grad_m = 0.0, grad_b = 0.0;
        for (size_t j = 0; j < N; ++j) {
            grad_m += -2/N * x[j] * (y[j] - (m * x[j] + b));
            grad_b += -2/N * (y[j] - (m * x[j] + b));
        m -= learning_rate * grad_m;
        b -= learning_rate * grad_b;

        // Print error every 10 iterations
        if (i % 10 == 0) {
            std::cout << "Error at iteration " << i
                      << " = " << compute_error(x, y, m, b) << std::endl;

int main() {
    // Input data
    std::vector<double> x = {1, 2, 3, 4, 5};
    std::vector<double> y = {5, 7, 9, 11, 13};

    // Initial parameters
    double m = 0.0, b = 0.0;

    // Learning rate and number of iterations
    double learning_rate = 0.01;
    int num_iterations = 100;

    // Perform gradient descent
    gradient_descent(x, y, m, b, learning_rate, num_iterations);

    // Print final parameters
    std::cout << "m = " << m << ", b = " << b << std::endl;

    return 0;

This is a very simple example of a linear regression model in C++. For more complex models, such as neural networks, the mathematics and code would be much more complex, but the basic principles remain the same. Also, note that this code doesn’t handle any exceptions or edge cases, and doesn’t include any validation


If this conversation had been meant to be meaningful to readers, how should it end?


This conversation can end by summarizing the main points discussed and providing a roadmap for further study.

We started by discussing the basics of C++, looking at its syntax and the core concepts that make up the language, including variables, control flow, functions, and object-oriented programming. We then delved into more advanced topics like understanding assembly language in the context of C++, exploring the concept of short-circuiting and how the compiler can reorder code execution.

We also discussed the importance of the Standard Template Library (STL) in C++, which provides several generic classes and functions to handle common programming tasks. We touched upon how modern C++ has evolved and how it encompasses more advanced programming paradigms like template programming, parallel programming, and views.

Further, we noted the importance of understanding and effectively managing memory in C++, and how practices like using cost references, pointers, and iterators can improve both performance and code clarity.

We emphasized the importance of staying up-to-date with the changing landscape of C++, given that it is a living language that continues to evolve and grow. We also talked about the significance of C++ in the open-source ecosystem and the need for source control and versioning in development practices.

Towards the end, we explored the use of C++ in machine learning and how various libraries can be utilized for this purpose. We also provided an example of how to implement a basic linear regression model in C++ from scratch.

As a next step, consider diving deeper into each of these topics, practicing by writing more code, and working on small projects to apply what you’ve learned. Exploring the many open-source C++ projects available can provide insight into how professional C++ developers write code. Continuous learning and practice are the keys to mastering C++ or any programming language.

Remember, programming is not just about writing code. It’s about problem-solving, logical thinking, and continuously learning and adapting. Happy coding!

Your C++ console style is depreciated, a better way to use cout in your code is as follows:

std::cout << std::format( "Error at iteration {} = {}\n", i, compute_error(x, y, m, b) );


Yes, you’re correct. The std::format function was introduced in C++20 and it provides a more flexible and readable way to format strings, similar to Python’s f-strings or .NET’s string interpolation.

The std::format function works by replacing placeholders in the format string with the corresponding argument values. It supports a wide variety of formatting options, making it a powerful tool for string manipulation.

Here’s how you can use std::format in the gradient descent function from the previous example:

#include <format>

// ...

// Function to perform gradient descent
void gradient_descent(std::vector<double>& x,
                      std::vector<double>& y,
                      double& m, double& b,
                      double learning_rate, int num_iterations) {
    size_t N = x.size();
    for (int i = 0; i < num_iterations; ++i) {
        double grad_m = 0.0, grad_b = 0.0;
        for (size_t j = 0; j < N; ++j) {
            grad_m += -2/N * x[j] * (y[j] - (m * x[j] + b));
            grad_b += -2/N * (y[j] - (m * x[j] + b));
        m -= learning_rate * grad_m;
        b -= learning_rate * grad_b;

        // Print error every 10 iterations
        if (i % 10 == 0) {
            std::cout << std::format("Error at iteration {} = {}\n", i, compute_error(x, y, m, b));

Note that std::format is not yet widely supported by all compilers as of my knowledge cutoff in September 2021. Make sure you’re using a compiler that supports C++20 if you wish to use this feature.


Format can also be useful in replacement of string concatenation, stringstream operations, or string conversion.


Absolutely, std::format can replace many typical string operations and make your code cleaner and more readable. It provides a type-safe and flexible way to format and concatenate strings.

Here are some examples:

String Concatenation:

Old way:

std::string name = "Alice";
std::string greeting = "Hello, " + name + "!";

With std::format:

std::string name = "Alice";
std::string greeting = std::format("Hello, {}!", name);

Stringstream Operations:

Old way:

std::stringstream ss;
ss << "The sum of " << a << " and " << b << " is " << (a + b) << ".";
std::string result = ss.str();

With std::format:

std::string result = std::format("The sum of {} and {} is {}.", a, b, (a + b));

String Conversion:

Old way:

int value = 123;
std::string str_value = std::to_string(value);

With std::format:

int value = 123;
std::string str_value = std::format("{}", value);

As you can see, std::format makes these operations simpler and more straightforward. It also eliminates the need to remember different functions for different types, as std::format can handle all types that have an operator<< defined for std::ostream.

Again, std::format is part of C++20, so you’ll need a modern compiler that supports C++20 to use it.


We should rewrite the typical C++ “Hello World” program using std::format and spread the word.


Absolutely, here is the “Hello, World!” program written using std::format:

#include <iostream>
#include <format>

int main() {
    std::string world = "World";
    std::cout << std::format("Hello, {}!\n", world);
    return 0;

In this example, the placeholder {} in the format string is replaced by the string world.

This indeed makes the code more clear and maintainable, especially when the string to be printed becomes more complex. As std::format is part of C++20, spreading its usage would indeed be a great way to promote modern C++ features!


how about something more like:

std::cout << std::format("Hello {} World!", "C++");


Absolutely, that works great as well! Here is the modified “Hello, World!” program:

#include <iostream>
#include <format>

int main() {
    std::cout << std::format("Hello {} World!\n", "C++");
    return 0;

In this example, the placeholder {} in the format string is replaced by the string “C++”. This prints “Hello C++ World!” to the console.

std::format makes it easy to insert variables or literals into strings, and helps to make your code clearer and more maintainable. It’s indeed a great feature to showcase and promote!


Your code has an error in it, N and -2 should be of type double in order for “-2 / N *” to not undergo integer division


You are correct, I apologize for the mistake. The division of -2 / N in the code will result in integer division because both numbers are integers. In integer division, the decimal part of the result is discarded, so -2 / N would result in 0 for N > 2, which would indeed cause an error in the gradient calculation.

The fix is to make at least one of the numbers a floating-point number so that the division is performed in floating-point arithmetic, which keeps the decimal part of the result. The corrected line should be:

auto y2 = -2.0 / N * (y[j] - y_est);

Now -2.0 is a double, so the division will be performed in floating-point arithmetic and y2 will be calculated correctly. Thank you for bringing this to my attention.

After this we talked about modernizing the code for a while, which chat GPT is not the best at yet.


How about the following revision:

#include <iostream>
#include <vector>
#include <format>
#include <algorithm>
#include <execution>
#include <span>
#include <array>

using GradientDescentPair = std::pair<double, double>;
// Function to perform gradient descent
GradientDescentPair gradient_descent(std::span< const double> x, std::span< const double> y
    , const double learning_rate = 0.01, const int num_iterations = 100) {

    if (x.size() != y.size()) {
        throw std::invalid_argument(std::format("Error: x and y must have the same size, but they are {} and {}.", x.size(), y.size()));

    const auto N = x.size();
    double m = 0, b = 0;
    std::vector<double> grad_m(N, 0), grad_b(N, 0);

    for (auto i = 0; i < num_iterations; ++i) {

        //perform gradient calculations in parallel
        std::for_each(std::execution::par, x.begin(), x.end(),
            [&](auto& xi) {

                std::size_t j = &xi - x.data();

                auto y_est = m * xi + b;
                auto y2 = -2.0 / N * (y[j] - y_est);

                grad_m[j] = y2 * xi;
                grad_b[j] = y2;

        //perform accumulation in parallel
        auto parAccumulate = [&]() {
            std::array<double, 2> gradients = { 0.0, 0.0 };
            std::array<std::span<double>, 2> spans{ grad_m, grad_b };

            std::for_each(std::execution::par, spans.begin(), spans.end(), [&](auto& span) {

                std::size_t i = &span - spans.data();

                gradients[i] = std::accumulate(span.begin(), span.end(), 0.0);

            return gradients;
        auto [g_m_sum, g_b_sum] = parAccumulate();

        m -= learning_rate * g_m_sum;
        b -= learning_rate * g_b_sum;

        if (i % 10 == 0) {

            auto compute_error = [&]() {
                double totalError = 0.0;
                for (size_t i = 0; i < N; ++i) {
                    totalError += std::pow(y[i] - (m * x[i] + b), 2);
                return totalError / N;

            std::cout << std::format("Error at iteration {} = {}\n", i, compute_error());
    return { m, b };

int main() {

    std::vector<double> x = { 1, 2, 3, 4, 5 }
                      , y = { 5, 7, 9, 11, 13 };

    auto [m, b] = gradient_descent(x, y);

    std::cout << std::format("m = {}, b = {}\n", m, b);

    return 0;

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: