Rvalue References

Understand std::move

Because std::move does nothing but cast its argument to an rvalue, there have been
suggestions that a better name for it might have been something like rvalue_cast.

//c++11
template<typename T> // in namespace std
typename remove_reference<T>::type&&
move(T&& param)
{
 using ReturnType = // alias declaration;
 typename remove_reference<T>::type&&;
 return static_cast<ReturnType>(param);
}

// C++14; still in
template<typename T>
decltype(auto) move(T&& param) // namespace std
{
 using ReturnType = remove_reference_t<T>&&;
 return static_cast<ReturnType>(param);
}

Distinguish universal references from rvalue
references.

• If a function template parameter has type T&& for a deduced type T, or if an object is declared using auto&&, the parameter or object is a universal reference.
• If the form of the type declaration isn’t precisely type&&, or if type deduction does not occur, type&& denotes an rvalue reference.
• Universal references correspond to rvalue references if they’re initialized with rvalues. They correspond to lvalue references if they’re initialized with lvalues.

void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // is a universal reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // is a universal reference
template<typename T>
void f(std::vector<T>&& param); // param is an rvalue reference
std::vector<int> v;
f(v); // error! can't bind lvalue to
 // rvalue reference
auto timeFuncInvocation =
 [](auto&& func, auto&&... params) // C++14
 {
 start timer;
 std::forward<decltype(func)>(func)( // invoke func
 std::forward<decltype(params)>(params)... // on params
 );
 stop timer and record elapsed time;
 };

Use std::move on rvalue references,
std::forward on universal references.

• Apply std::move to rvalue references and std::forward to universal refer‐
ences the last time each is used.
• Do the same thing for rvalue references and universal references being
returned from functions that return by value.
• Never apply std::move or std::forward to local objects if they would other‐
wise be eligible for the return value optimization.

class Widget {
public:
 Widget(Widget&& rhs) // rhs is rvalue reference
 : name(std::move(rhs.name)),
 p(std::move(rhs.p))
 { … }
 …
private:
 std::string name;
 std::shared_ptr<SomeDataStructure> p;
};
class Widget {
public:
 template<typename T>
 void setName(T&& newName) // newName is
 { name = std::forward<T>(newName); } // universal reference
 …
};

using std::move with universal references,that can have the
effect of unexpectedly modifying lvalues

Matrix // by-value return
operator+(Matrix&& lhs, const Matrix& rhs)
{
 lhs += rhs;
 return std::move(lhs); // move lhs into
} 

Matrix // as above
operator+(Matrix&& lhs, const Matrix& rhs)
{
 lhs += rhs;
 return lhs; // copy lhs into
} 

template<typename T>
Fraction // by-value return
reduceAndCopy(T&& frac) // universal reference param
{
 frac.reduce();
 return std::forward<T>(frac); // move rvalue into return
} 

don’t move local variable into return value

Widget makeWidget() // Moving version of makeWidget
{
 Widget w;
 …
 return std::move(w); // move w into return value
} 

Avoid overloading on universal references

std::multiset<std::string> names; // global data structure
void logAndAdd(const std::string& name)
{
 auto now = // get current time
 std::chrono::system_clock::now();
 log(now, "logAndAdd"); // make log entry
 names.emplace(name); // add name to global data
} 
std::string petName("Darla");
logAndAdd(petName); // pass lvalue std::string
logAndAdd(std::string("Persephone")); // pass rvalue std::string
logAndAdd("Patty Dog"); // pass string literal

in the previous example in second and third call name itself is an lvalue, so it’s copied into names.

template<typename T>
void logAndAdd(T&& name)
{
 auto now = std::chrono::system_clock::now();
 log(now, "logAndAdd");
 names.emplace(std::forward<T>(name));
}
std::string petName("Darla"); // as before
logAndAdd(petName); // as before, copy lvalue into multiset
logAndAdd(std::string("Persephone")); // move rvalue instead of copying it
logAndAdd("Patty Dog"); // create std::string in multiset instead of copying a temporary std::string

Constraining templates that take universal references

Alternatives to the combination of universal references and overloading
include the use of distinct function names, passing parameters by lvaluereference-to-const, passing parameters by value, and using tag dispatch.
• Constraining templates via std::enable_if permits the use of universal references and overloading together, but it controls the conditions under which compilers may use the universal reference overloads.
• Universal reference parameters often have efficiency advantages, but they typically have usability disadvantages.

class Person {
public:
 template<
 typename T,
 typename = typename std::enable_if<!std::is_base_of<Person,typename std::decay<T>::type>::value>::type>
 explicit Person(T&& n);
 …
};

//c++14
class Person {
public:
 template<
 typename T,
 typename = std::enable_if_t<!std::is_base_of<Person,std::decay_t<T>>::value>>
 explicit Person(T&& n);
 …
};

#include <type_traits>

template<typename T>
class YourClass {

    YourClass() {
        // Compile-time check
        static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");

        // ...
    }
}
class Person {
public:
    template<typename T,typename = std::enable_if_t<std::is_base_of_v<Person,std::decay_t<T>>>>
    explicit Person(T&& n){
    }
    Person()= default;
};
int main(int argc,char* argv[]){
    Person pp;
    Person p(pp);
    return 0;
}

platformio configuration file

platformio.ini (configuration file)

Each PlatformIO project has a configuration file named platformio.ini in the root directory for the project.
platformio.ini has sections (each denoted by a [header]) and key / value pairs within the sections. Lines beginning with ; are ignored and may be used to provide comments.
Multiple value options can be specified in two ways:

  1. Split values with “, ” (comma + space)
  2. Multi-line format, where each new line starts with at least two spaces

there are two main sections
1- PlatformIO Core (CLI) settings: Section [platformio]
2- Environment settings: Section [env]

[platformio]

description : Short description of the project

default_envs : The platformio run command processes all environments [env:***] by default if the platformio run --environment option is not specified. 

extra_configs : This option allows extending a base “platformio.ini” (Project Configuration File) with extra configuration files.

Base “platformio.ini”

[platformio]
description = this is new project 
default_envs = esp32dev
extra_configs =
  extra_envs.ini
  extra_debug.ini

; Global data for all [env:***]
[env]
platform = espressif32
framework = espidf

; Custom data group
; can be use in [env:***] via ${common.***}
[common]
debug_flags = -D RELEASE
lib_flags = -lc -lm

[env:esp-wrover-kit]
board = esp-wrover-kit
build_flags = ${common.debug_flags}

extra_envs.ini

[env:esp32dev]
board = esp32dev
build_flags = ${common.lib_flags} ${common.debug_flags}

[env:lolin32]
platform = espressif32
framework = espidf
board = lolin32
build_flags = ${common.debug_flags}

src_dir: The path to the project’s directory with source code. Default: “Project/src
lib_dir : You can put your own/private libraries here. The source code of each library should be placed in a separate directory Default: “Project/data

For example, see how the Foo and Bar libraries are organized:

|--lib
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|- platformio.ini
|--src
   |- main.c

Then in src/main.c you should use:

#include <Foo.h>
#include <Bar.h>

[env]

Each project may have multiple configuration environments defining the available project tasks for building, programming, debugging, unit testing, device monitoring, library dependencies, etc.

Common [env] : An optional configuration environment with common options that will be shared between all [env:NAME] environments in the platform.ini file.
example:

[env]
platform = ststm32
framework = stm32cube
board = nucleo_l152re
lib_deps = Dep1, Dep2

[env:release]
build_flags = -D RELEASE
lib_deps =
    ${env.lib_deps}
    Dep3

[env:debug]
build_type = debug
build_flags = -D DEBUG
lib_deps = DepCustom

Environment [env:NAME] : A section with an env: prefix defines a working environment for platformio runplatformio testplatformio checkplatformio debug and other commands. Multiple [env:NAME] environments with different NAME are allowed. Every project must define at least one working environment.
Each environment must have a unique NAME. The valid chars for NAME are letters a-z, numbers 0-9, special char _ (underscore). For example, [env:hello_world].

you can use options inside [env] the next link for all available options
https://docs.platformio.org/en/latest/projectconf/section_env.html#options

Dynamic variables

Dynamic variables (interpolations) are useful when you have a custom configuration data between build environments. For examples, extra build_flags or project dependencies lib_deps.

Each variable should have a next format: ${<section>.<option>}, where <section> is a value from [<section>] group, and <option> is a first item from pair <option> = value.

You can inject system environment variable using sysenv as a section. For example, ${sysenv.HOME}.

  • Variable can be applied only for the option’s value
  • Multiple variables are allowed
  • The Section [platformio] and Section [env] sections are reserved and could not be used as a custom section. Some good section names might be extra or custom.
[env]
; Unix
lib_extra_dirs = ${sysenv.HOME}/Documents/Arduino/libraries
; Windows
lib_extra_dirs = ${sysenv.HOMEDRIVE}${sysenv.HOMEPATH}\Documents\Arduino\libraries

; You MUST inject these options into [env:] section
; using ${extra.***} (see below)
[extra]
build_flags = -D VERSION=1.2.3 -D DEBUG=1
lib_deps_builtin =
  SPI
  Wire
lib_deps_external = ArduinoJson@>5.6.0

[env:uno]
platform = atmelavr
framework = arduino
board = uno
build_flags = ${extra.build_flags}
lib_deps =
  ${extra.lib_deps_builtin}
  ${extra.lib_deps_external}

[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
build_flags = ${extra.build_flags} -Wall
lib_deps =
  ${extra.lib_deps_builtin}
  ${extra.lib_deps_external}
  PubSubClient@2.6
  OneWire

; Keep sensitive data in environment variables
;
; Unix
; export WIFI_SSID='\"my\ ssid\ name\"'
; export WIFI_PASS='\"my\ password\"'
;
; Windows
; set WIFI_SSID='"my ssid name"'
; set WIFI_PASS='"my password"'

[env:esp32dev]
extends = env:nodemcuv2
platform = espressif32
board = esp32dev
build_flags =
  -DWIFI_SSID=${sysenv.WIFI_SSID}
  -DWIFI_PASS=${sysenv.WIFI_PASS}

ESP32 ADC and DAC

ADC

ADC is Non-linear Ideally, you would expect a linear behavior when using the ESP32 ADC pins. However, that doesn’t happen. What you’ll get is behavior as shown in the following chart:

typical inputs are VP or VN or others 16 pins so The ESP32 supports measurements in 18 different channels
The voltage measured is then assigned to a value between 0 and 4095(12 bits), in which 0 V corresponds to 0, and 3.3 V corresponds to 4095. Any voltage between 0 V and 3.3 V will be given the corresponding value in between.

the easiest way to use adc is

analogRead(GPIO);
analogReadResolution(resolution)//set the sample bits and resolution. It can be a value between 9 (0 – 511) and 12 bits (0 – 4095). Default is 12-bit resolution.

but actually there are other useful functions

analogSetWidth(width): set the sample bits and resolution. It can be a value between 9 (0 – 511) and 12 bits (0 – 4095). Default is 12-bit resolution.
analogSetCycles(cycles): set the number of cycles per sample. Default is 8. Range: 1 to 255.
analogSetSamples(samples): set the number of samples in the range. Default is 1 sample. It has an effect of increasing sensitivity.
analogSetClockDiv(attenuation): set the divider for the ADC clock. Default is 1. Range: 1 to 255.
analogSetAttenuation(attenuation): sets the input attenuation for all ADC pins. Default is ADC_11db. Accepted values:
ADC_0db: sets no attenuation (1V input = ADC reading of 1088).
ADC_2_5db: sets an attenuation of 1.34 (1V input = ADC reading of 2086).
ADC_6db: sets an attenuation of 1.5 (1V input = ADC reading of 2975).
ADC_11db: sets an attenuation of 3.6 (1V input = ADC reading of 3959).
analogSetPinAttenuation(pin, attenuation): sets the input attenuation for the specified pin. The default is ADC_11db. Attenuation values are the same from previous function.
adcAttachPin(pin): Attach a pin to ADC (also clears any other analog mode that could be on). Returns TRUE or FALSE result.
adcStart(pin), adcBusy(pin) and adcEnd(pin): starts an ADC convertion on attached pin’s bus. Check if conversion on the pin’s ADC bus is currently running (returns TRUE or FALSE). Get the result of the conversion: returns 16-bit integer.

DAC

there are two DAC GPIO in esp32 GPIO25 and GPIO26

#define DAC1 25
 
void setup() {
  Serial.begin(115200);
  
}
 
void loop() { // Generate a Sine wave
  int Value = 255; //255= 3.3V 128=1.65V
  
  dacWrite(DAC1, Value);
  delay(1000);
}

Moving to Modern C++

Distinguish between () and {} when creating objects

class Widget {
public:
 Widget(int i, bool b); // ctors not declaring
 Widget(int i, double d); // std::initializer_list params
 …
};
Widget w1(10, true); // calls first ctor
Widget w2{10, true}; // also calls first ctor
Widget w3(10, 5.0); // calls second ctor
Widget w4{10, 5.0}; // also calls second ctor
class Widget {
public:
 Widget(int i, bool b); // as before
 Widget(int i, double d); // as before
Widget(std::initializer_list<long double> il); // added
 …
};
Widget w1(10, true); // uses parens and, as before,
 // calls first ctor
Widget w2{10, true}; // uses braces, but now calls
 // std::initializer_list ctor
// (10 and true convert to long double)
Widget w3(10, 5.0); // uses parens and, as before,
 // calls second ctor
Widget w4{10, 5.0}; // uses braces, but now calls
 // std::initializer_list ctor
// (10 and 5.0 convert to long double)
class Widget {
public:
 Widget(int i, bool b); // as before
 Widget(int i, double d); // as before
 Widget(std::initializer_list<long double> il); // as before
 operator float() const; // convert
 … // to float
};
Widget w5(w4); // uses parens, calls copy ctor
Widget w6{w4}; // uses braces, calls std::initializer_list ctor (w4 converts to float, and float converts to long double)
Widget w7(std::move(w4)); // uses parens, calls move ctor
Widget w8{std::move(w4)}; // uses braces, calls std::initializer_list ctor (for same reason as w6)
class Widget {
public:
 Widget(int i, bool b); // as before
 Widget(int i, double d); // as before
 // std::initializer_list element type is now std::string
 Widget(std::initializer_list<std::string> il);
 … // no implicit
}; // conversion funcs
Widget w1(10, true); // uses parens, still calls first ctor
Widget w2{10, true}; // uses braces, now calls first ctor
Widget w3(10, 5.0); // uses parens, still calls second ctor
Widget w4{10, 5.0}; // uses braces, now calls second ctor
class Widget {
public:
 Widget(); // default ctor
 Widget(std::initializer_list<int> il); // std::initializer
 // _list ctor
 … // no implicit
}; // conversion funcs
Widget w1; // calls default ctor
Widget w2{}; // also calls default ctor
Widget w3(); // most vexing parse! declares a function!
Widget w4({}); // calls std::initializer_list ctor with empty list
Widget w5{{}}; // ditto
std::vector<int> v1(10, 20); // use non-std::initializer_list
 // ctor: create 10-element
// std::vector, all elements have
// value of 20
std::vector<int> v2{10, 20}; // use std::initializer_list ctor:
 // create 2-element std::vector,
// element values are 10 and 20
template<typename T, // type of object to create
 typename... Ts> // types of arguments to use
void doSomeWork(Ts&&... params)
{
 create local T object from params...
 …
}
T localObject(std::forward<Ts>(params)...); // using parens
T localObject{std::forward<Ts>(params)...}; // using braces

Prefer nullptr to 0 and NULL

void f(int); // three overloads of f
void f(bool);
void f(void*);
f(0); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls
 // f(int). Never calls f(void*)
f(nullptr); // calls f(void*) overload

Prefer alias declarations to typedefs

Avoiding such medical tragedies is easy. Introduce a typedef:

typedef std::unique_ptr> UPtrMapSS;

But typedefs are soooo C++98. They work in C++11, sure, but C++11 also offers alias declarations:

using UPtrMapSS = std::unique_ptr>;
// FP is a synonym for a pointer to a function taking an int and
// a const std::string& and returning nothing
typedef void (*FP)(int, const std::string&); // typedef
// same meaning as above
using FP = void (*)(int, const std::string&); // alias
 // declaration
template<typename T> // MyAllocList<T>
using MyAllocList = std::list<T, MyAlloc<T>>; // is synonym for
 // std::list<T,
// MyAlloc<T>>
MyAllocList<Widget> lw; // client code

template<typename T> // MyAllocList<T>::type
struct MyAllocList { // is synonym for
 typedef std::list<T, MyAlloc<T>> type; // std::list<T,
}; // MyAlloc<T>>
MyAllocList<Widget>::type lw; // client code

• typedefs don’t support templatization, but alias declarations do.
• Alias templates avoid the “::type” suffix and, in templates, the “typename”
prefix often required to refer to typedefs

template <class T>
using remove_const_t = typename remove_const<T>::type;
template <class T>
using remove_reference_t = typename remove_reference<T>::type;
template <class T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;

Prefer scoped enums to unscoped enums

enum Color { black, white, red }; // black, white, red are in same scope as Color
auto white = false; // error! white already declared in this scope

enum class Color { black, white, red }; // black, white, red are scoped to Color
auto white = false; // fine, no other
Color c = white; // error! no enumerator named "white" is in this scope
Color c = Color::white; // fine
auto c = Color::white; // also fine (and in accord with Item 5's advice)
enum Color { black, white, red }; // unscoped enum
std::vector<std::size_t> // func. returning
 primeFactors(std::size_t x); // prime factors of x
Color c = red;
…
if (c < 14.5) { // compare Color to double (!)
 auto factors = // compute prime factors
 primeFactors(c); // of a Color (!)
 …
}
enum class Color { black, white, red }; // enum is now scoped
Color c = Color::red; // as before, but
… // with scope qualifier
if (c < 14.5) { // error! can't compare Color and double
auto factors = // error! can't pass Color to
 primeFactors(c); // function expecting std::size_t
 …
}
if (static_cast<double>(c) < 14.5) { // odd code, but
 // it's valid
 auto factors = // suspect, but
 primeFactors(static_cast<std::size_t>(c)); // it compiles
 …
}

Prefer deleted functions to private undefined ones.

template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
 …
private:
 basic_ios(const basic_ios& ); // not defined
 basic_ios& operator=(const basic_ios&); // not defined
};
//instead prefer to use delete
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
 …
 basic_ios(const basic_ios& ) = delete;
 basic_ios& operator=(const basic_ios&) = delete;
 …
};
void fun(int k)
void fun(char)=delete;
void fun(double)=delete;
fun(12);//ok
fun('a');//error
fun(4.5);//error
class Widget {
public:
 …
 template<typename T>
 void processPointer(T* ptr)
 { … }
 …
};
template<> // still
void Widget::processPointer<void>(void*) = delete; // public, but deleted

Deducing Types

Template

template<typename T>
void f(T& param); // param is a reference
//and we have these variable declarations,
int x = 27; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
//the deduced types for param and T in various calls are as follows:
f(x); // T is int, param's type is int&
f(cx); // T is const int,
 // param's type is const int&
f(rx); // T is const int,
 // param's type is const int&

universal reference

template<typename T>
void f(T&& param); // param is now a universal reference
int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // x is lvalue, so T is int&, param's type is also int&
f(cx); // cx is lvalue, so T is const int&, param's type is also const int&
f(rx); // rx is lvalue, so T is const int&, param's type is also const int&
f(27); // 27 is rvalue, so T is int,param's type is therefore int&&

not pointer or reference

template<typename T>
void f(T param); // param is now passed by value
int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // T's and param's types are both int
f(cx); // T's and param's types are again both int
f(rx); // T's and param's types are still both int
template<typename T>
void f(T param); // param is still passed by value
const char* const ptr = // ptr is const pointer to const object "Fun with pointers";
f(ptr); // pass arg of type const char * const

Array Arguments

template<typename T, std::size_t N> // see info
constexpr std::size_t arraySize(T (&)[N]) noexcept // below on
{
    return N; // and
} 
int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 }; // keyVals has 7 elements
int mappedVals[arraySize(keyVals)]; // so does mappedVals

Auto

auto x = 27; // case 3 (x is neither ptr nor reference)
const auto cx = x; // case 3 (cx isn't either)
const auto& rx = x; // case 1 (rx is a non-universal ref.)
auto&& uref1 = x; // x is int and lvalue,  so uref1's type is int&
auto&& uref2 = cx; // cx is const int and lvalue,  so uref2's type is const int&
auto&& uref3 = 27; // 27 is int and rvalue,  so uref3's type is int&&
const char name[] ="R. N. Briggs"; // name's type is const char[13]
auto arr1 = name; // arr1's type is const char*
auto& arr2 = name; // arr2's type is
void someFunc(int, double); // someFunc is a function;  type is void(int, double)
auto func1 = someFunc; // func1's type is void (*)(int, double)
auto& func2 = someFunc; // func2's type is void (&)(int, double)
auto x5 = { 1, 2, 3.0 }; // error! can't deduce T for std::initializer_list<T>

Decltype

const int i = 0; // decltype(i) is const int
bool f(const Widget& w); // decltype(w) is const Widget& decltype(f) is bool(const Widget&)
struct Point {
 int x, y; // decltype(Point::x) is int
}; // decltype(Point::y) is int
Widget w; // decltype(w) is Widget
if (f(w)) … // decltype(f(w)) is bool
template<typename T> // simplified version of std::vector
class vector {
public:
 …
 T& operator[](std::size_t index);
 …
};
vector<int> v; // decltype(v) is vector<int>
…
if (v[0] == 0) … // decltype(v[0]) is int&
template<typename Container, typename Index> // works, but requires refinement
auto authAndAccess(Container& c, Index i) -> decltype(c[i]) 
{
 authenticateUser();
 return c[i];
}
template<typename Container, typename Index> // C++14;
auto authAndAccess(Container& c, Index i) // not quite
{ // correct
 authenticateUser();
 return c[i]; // return type deduced from c[i]
}
std::deque<int> d;
…
authAndAccess(d, 5) = 10; // authenticate user, return d[5],
 // then assign 10 to it;
// this won't compile!

template<typename Container, typename Index> // C++14; works,but still requires refinement
decltype(auto) 
authAndAccess(Container& c, Index i) 
{ 
 authenticateUser();
 return c[i];
}

Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // auto type deduction: myWidget1's type is Widget
decltype(auto) myWidget2 = cw; // decltype type deduction: myWidget2's type 
    int i=12;
    decltype(i) ii=i;//value
    decltype((i)) iii=i;//reference
    decltype(auto) iiii=i;//reference
decltype(auto) f1()
{
 int x = 0;
 …
 return x; // decltype(x) is int, so f1 returns int
}
decltype(auto) f2()
{
 int x = 0;
 …
 return (x); // decltype((x)) is int&, so f2 returns int&
}

why auto

template<typename It> // algorithm to dwim ("do what I mean")
void dwim(It b, It e) // for all elements in range from
{ // b to e
 while (b != e) {
 typename std::iterator_traits<It>::value_type currValue = *b;
 }
}

and you can use auto instead

template<typename It> // as before
void dwim(It b, It e)
{
 while (b != e) {
 auto currValue = *b;
 …
 }
auto derefUPLess = // comparison func.
 [](const std::unique_ptr<Widget>& p1, // for Widgets
 const std::unique_ptr<Widget>& p2) // pointed to by
 { return *p1 < *p2; }; // std::unique_ptrs
//use auto for all 
auto derefLess = // C++14 comparison
 [](const auto& p1, // function for
 const auto& p2) // values pointed
 { return *p1 < *p2; }; // to by anything
 // pointer-like

CONSTEXPR

Declare functions noexcept if they won’t emit exceptions

Values known during compilation are privileged. They may be placed in read-only memory, for example, and, especially for developers of embedded systems, this can be a feature of considerable importance. Of broader applicability is that integral val‐ ues that are constant and known during compilation can be used in contexts where C++ requires an integral constant expression. Such contexts include specification of array sizes, integral template arguments (including lengths of std::array objects), enumerator values, alignment specifiers, and more. If you want to use a variable for these kinds of things, you certainly want to declare it constexpr, because then com‐ pilers will ensure that it has a compile-time value:

int sz; // non-constexpr variable … 
constexpr auto arraySize1 = sz; // error! sz's value not known at compilation 
std::array data1; // error! same problem 
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

Note that const doesn’t offer the same guarantee as constexpr, because const
objects need not be initialized with values known during compilation:

int sz; // as before
…
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value not known at compilation

Simply put, all constexpr objects are const, but not all const objects are constexpr.
If you want compilers to guarantee that a variable has a value that can beused in contexts requiring compile-time constants, the tool to reach for is constexpr, not const.

constexpr functions can be used in contexts that demand compile-time con‐
stants. If the values of the arguments you pass to a constexpr function in such a context are known during compilation, the result will be computed during compilation. If any of the arguments’ values is not known during compilation,your code will be rejected.

When a constexpr function is called with one or more values that are not
known during compilation, it acts like a normal function, computing its result at runtime. This means you don’t need two functions to perform the same operation, one for compile-time constants and one for all other values. The constexpr function does it all.

constexpr // pow's a constexpr func
int pow(int base, int exp) noexcept // that never throws
{
 … // impl is below
}
constexpr auto numConds = 5; // # of conditions
std::array<int, pow(3, numConds)> results; // results has 3^numConds elements
auto base = readFromDB("base"); // get these values
auto exp = readFromDB("exponent"); // at runtime
auto baseToExp = pow(base, exp); // call pow function at runtime
constexpr int pow(int base, int exp) noexcept
{
 return (exp == 0 ? 1 : base * pow(base, exp - 1));
}

constexpr functions are limited to taking and returning literal types, which essentially means types that can have values determined during compilation. In C++11, all built-in types except void qualify, but user-defined types may be literal, too, because constructors and other member functions may be constexpr:

class Point {
public:
 constexpr Point(double xVal = 0, double yVal = 0) noexcept
 : x(xVal), y(yVal)
 {}
 constexpr double xValue() const noexcept { return x; }
 constexpr double yValue() const noexcept { return y; }
 void setX(double newX) noexcept { x = newX; }
 void setY(double newY) noexcept { y = newY; }
private:
 double x, y;
};
constexpr Point p1(9.4, 27.7); // fine, "runs" constexpr ctor during compilation
constexpr
Point midpoint(const Point& p1, const Point& p2) noexcept
{
 return { (p1.xValue() + p2.xValue()) / 2, // call constexpr
 (p1.yValue() + p2.yValue()) / 2 }; // member funcs
}
constexpr auto mid = midpoint(p1, p2); // init constexpr object w/result of constexpr function

In C++11, two restrictions prevent Point’s member functions setX and setY from being declared constexpr. First, they modify the object they operate on, and in C++11, constexpr member functions are implicitly const. Second, they have void return types, and void isn’t a literal type in C++11. Both these restrictions are lifted in C++14, so in C++14, even Point’s setters can be constexpr:

class Point {
public:
 …
 constexpr void setX(double newX) noexcept // C++14
 { x = newX; }
 constexpr void setY(double newY) noexcept // C++14
 { y = newY; }
 …
};
constexpr Point reflection(const Point& p) noexcept
{
 Point result; // create non-const Point
 result.setX(-p.xValue()); // set its x and y values
 result.setY(-p.yValue());
 return result; // return copy of it
}
constexpr Point p1(9.4, 27.7); // as above
constexpr Point p2(28.8, 5.3);
constexpr auto mid = midpoint(p1, p2);
constexpr auto reflectedMid = // reflectedMid's value is
 reflection(mid); // (-19.1 -16.5) and known  during compilation