14 KiB
Cpp11
-
A quick reminder of lesser known features
- static_assert
- Uniform initialization
- In-class initialization of non-static variables
- initializer_list
- alias
- Template alias
- C'tor inheritance
- Attributes
- Data structure alignment
static_assert
template <class T>
void swap(T& a, T& b)
{
static_assert(std::is_copy_constructible<T>::value,
"Swap requires copying");
static_assert(std::is_nothrow_move_constructible_v<T> &&
std::is_nothrow_move_assignable_v<T>);
auto c = b;
b = a;
a = c;
}
Rationale: Preventing compilation on user defined conditions (usually specific types).
Performs compile-time assertion checking. Usually used with <type_traits> library.
The message is optional from C++17.
C++98/03 initialization
int a; // undefined value
int b(5); // direct initialization, b = 5
int c = 10; // copy initialization, c = 10
int d = int(); // default initialization, d = 0
int e(); // function declaration - "most vexing parse"
int values[] = { 1, 2, 3, 4 }; // brace initialization of aggregate
int array[] = { 1, 2, 3.5 }; // C++98 - ok, implicit type narrowing
struct P { int a, b; };
P p = { 20, 40 }; // brace initialization of POD
std::complex<double> c(4.0, 2.0); // initialization of classes
std::vector<std::string> names; // no initialization for list of values
names.push_back("John");
names.push_back("Jane");
C++11 initialization with
int a; // still undefined value
int b{5}; // brace initialization, b = 5
int c{}; // brace initialization, c = 0
int values[] = { 1, 2, 3, 4 }; // brace initialization of aggregate
int array[] = { 1, 2, 3.5 }; // C++11: error - implicit type narrowing
struct P { int a, b;
P p = { 20, 40 }; // brace initialization of POD
std::complex<double> c{4.0, 2.0}; // brace initialization calls adequate c-tor
std::vector<std::string> names = { "John", "Jane" };
// brace initialization of vector
Rationale: eliminate problematic initialization cases from C++98, initialization of STL containers, have one universal way of initialization.
In-class initialization of non-static variables
struct Foo
{
Foo() {}
Foo(std::string a) : a_(a) {}
void print() { std::cout << a_ << std::endl; }
private:
std::string a_ = "Foo"; // C++98: error, C++11: OK
static const unsigned VALUE = 20u; // C++98: OK, C++11: OK
};
Foo().print(); // Foo
Foo("Bar").print(); // Bar
std::initializer_list<T>
auto values = {1, 2, 3, 4, 5}; // values is std::initializer_list<int>
std::vector<int> v = {1, 2, -3}; // creates a vector from
// std::initializer_list<int>
-
Defined in
initializer_listheader - Elements are kept in an array
- Elements are immutable
- Elements must be copyable
-
Have limited interface and access via iterators -
begin(),end(),size() - Should be passed to functions by value
Constructor priority
template<class Type>
class Bar {
std::vector<Type> values_;
public:
Bar(std::initializer_list<Type> values) : values_(values) {}
Bar(Type a, Type b) : values_{a, b} {}
};
Bar<int> c = {1, 2, 5, 51}; // calls std::initializer_list c-tor
Bar<int> d{1, 2, 5, 51}; // calls std::initializer_list c-tor
Bar<int> e = {1, 2}; // calls std::initializer_list c-tor
Bar<int> f{1, 2}; // calls std::initializer_list c-tor
Bar<int> g(1, 2); // calls Bar(Type a, Type b) c-tor
Bar<int> h = {}; // calls std::initializer_list c-tor
// or default c-tor if exists
Bar<std::unique_ptr> c = {new int{1}, new int{2}};
// error - std::unique_ptr is non-copyable
C-tor with std::initializer_list has greater priority, even if other c-tors match.
Exercise 1
- Open project streamer
-
Add two C'tor:
-
first will take
initializer_list -
second
const StreamInfo&
-
first will take
-
initialize
vectorininitialization list
Type aliasing
typedef std::ios_base::fmtflags Flags;
using Flags = std::ios_base::fmtflags; // the same as above
Flags fl = std::ios_base::dec;
typedef std::vector<std::shared_ptr<Socket>> SocketContainer;
std::vector<std::shared_ptr<Socket>> typedef SocketContainer; // correct ;)
using SocketContainer = std::vector<std::shared_ptr<Socket>>;
Rationale: More intuitive alias creation.
A type alias is a name that refers to a previously defined type. It could be created with typedef.
From C++11 type aliases should be created with using keyword.
Template aliases
struct ThemedLabelToogleButton { ... }
template <typename T>
using ButtonMap = std::map<ThemedLabelToogleButton, T>;
ButtonMap<std::function<void()>> my_map;
// std::map<ThemedLabelToogleButton, std::function<void()>
Type alias can be parametrized with templates. It was impossible with typedef.
Template aliases cannot be specialized.
Constructors inheritance
struct A {
explicit A(int);
int a;
};
struct B : A {
using A::A; // implicit declaration of B::B(int)
B(int, int); // overloaded inherited Base ctor
};
- Derived class constructors are generated implicitly, only if they are used
- Derived class constructors take the same arguments as base class constructors
- Derived class constructor calls according base class constructor
- Constructor inheritance in a class that adds a new field might be risky - new fields can be uninitialized
Exercise 2
- Open project streamer
- Add aliases for ip adress, port and vlan.
Attributes
Standard attributes
-
[[noreturn]]- function does never return, likestd::terminate. If it does, we have UB -
[[deprecated]](C++14) - function is deprecated -
[[deprecated("reason")]](C++14) - as above, but compiler will emit the reason -
[[fallthrough]](C++17) - inswitchstatement, indicated that fall through is intentional -
[[nodiscard]](C++17) - you cannot ignore value returned from function -
[[maybe_unused]](C++17) - suppress compiler warning on unused class, typedef, variable, function, etc.
[[noreturn]] attribute
[[noreturn]] void f() {
throw "error";
// OK
}
[[noreturn]] void q(int i) {
if (i > 0) {
throw "positive";
}
// the behavior is undefined if called with argument <=0
}
[[deprecated]] attribute
Attributes for namespaces and enumerators are available from C++17.
[[deprecated("Please use f2 instead")]] int f1();
enum E {
foo = 0,
bar [[deprecated]] = foo
};
E e = bar; // Emits warning
namespace [[deprecated]] old_stuff {
void legacy();
}
old_stuff::legacy(); //Emits warning
[[fallthrough]] attribute
void f(int n){
void g(), h(), i();
switch(n) {
case 1:
case 2:
g();
[[fallthrough]];
case 3: // no warning on fallthrough
h();
case 4: // compiler may warn on fallthrough
i();
[[fallthrough]]; // illformed, not before a case label
}
}
[[nodiscard]] attribute
struct [[nodiscard]] error_info {};
error_info process(Data*);
// ...
void passMessage() {
auto data = getData();
process(data); // compiler warning, discarding error_info
}
[[maybe_unused]] attributes
[[maybe_unused]] void f([[maybe_unused]] bool thing1,
[[maybe_unused]] bool thing2)
{
[[maybe_unused]] bool b = thing1 && thing2;
assert (b); // in release mode, assert is compiled out, and b is unused
// no warning because it is declared [[maybe_unused]]
} // parameters thing1 and thing2 are not used, no warning