int arr[1];
int *p;
arr = xxx; // Error
p = xxx; // OK
int arr[10];
int *p = (int*)malloc(sizeof(int)*10);
size_t as = sizeof(arr); // 40
size_t ps = sizeof(p); // 4
// Only valid in C99 and optional C11 (GNU11)
int a = 100;
int v[a] = {0};
sizeof(v); // 400! runtime evaluation
struct Foo {
Foo(const Foo &) = delete;
};
int main() { Foo bar{}, b2{ Foo{} }; } // OK
class Foo {
Foo(const Foo &) = delete;
};
int main() { Foo bar{}, b2{ Foo{} }; } // OK
struct Foo {
explicit Foo(const Foo &) = delete;
};
int main() { Foo bar{}, b2{ Foo{} }; } // Error
struct Foo {
Foo() = delete;
};
int main() { Foo bar{}; } // OK
struct Foo {
Foo() = delete;
Foo(int);
};
int main() { Foo bar{}; } // Error
struct Foo {
private:
Foo();
};
int main() { Foo bar{}; } // Error
static int a = 1;
static int b = 2;
initializer_list<int> foo() {
return {a, b} // copy first, return (assign by pointer)
}
int main() {
auto p = foo(); // Error: dangling pointer
}
template <typename T>
T foo1() {
return T();
}
template <typename T>
T foo2() {
return T{};
}
template <typename T>
void foo3() {
auto p = new T{};
delete p;
}
void bar() {
return void(); // OK
}
void baz() {
return void{}; // Error
}
- std::invoke with return void
char* \U0001f431 = "cat" // OK
char* 😺 = "cat" // Error in GCC
int abc = 1;
int ab\u200c = 1;
int a\u200bc = 1;
int abc\u200 = 1;
template<class T>
template<typename F, typename ...Args>
T Container<T>::addTask(F&& func, Args&&... args)
{
container.emplace_back( [func = std::forward<F>(func),
args = std::make_tuple(std::forward<ARGS>(args)...) ]
() mutable // make mutable if you want to move the args in to func
{
return std::apply(func, std::move(args));
});
//.....
}
- vector erase-remove
- remove_if
- dis
std::unordered_map<...> m;
auto it = m.begin();
while (it != m.end())
{
if (pred(*it))
it = m.erase(it);
else
++it;
} // UB before Cpp14
std::unordered_map<...> mymap;
std::vector<decltype(mymap)::key_type> vec;
for (auto&& i : mymap)
if (/*compare i*/)
vec.emplace_back(i.first);
for (auto&& key : vec)
mymap.erase(key);
template <typename T>
template <typename U>
using P = std::is_same<T, U>; // Usable in gcc, failed in clang
// ill-formed
int a = 1, b = 2, c = 3;
std::tie(a, b, c) = {c, a, b};
cout << a << ' ' << b; // 3 3 well-defined?
sizeof('a' + ' ') == sizeof(int) // true (C even char/bool literal = int)
-1L < 1U // long > int -> convert to long 1 | long == int -> convert to unsigned long 0
- ref: SuperNaiveCppLib/notes/CppCon2017/Notes
- make compiler remove const assumption
struct X {
const int n;
const double d;
};
X* p = new X{7, 8.8};
new (p) X{42, 9.9}; // place new value into p
int b = std::launder(p)->n; // OK, b is 42
int c = p->n; // undefined behavior!
double d = p->d; // undefined behavior!
struct foo {
foo() = default;
foo(cosnt foo&) = delete;
};
int main() {
auto x = foo(); // prev-17: error! | 17: prvalue copy elision
}
class A {
A(const A&) = default;
A& operator(const A& rhs) {
delete();
A temp(rhs);
std::swap(*this, temp); // infinite loop! will call copy ctor
return *this;
}
};
const_cast<char*>("WTF")[0] = 'd'; // OK, though segmentation fault
true = false; // Failed, 1 is prvalue. <del>Python 2 will allow this</del>
class A {};
#define A(x) _
int main() {
A a{}; // OK
A(a{}); // Error
}
char c = 'X';
int& i = c; // Error: promotion causes a prvalue
i = 'J';
void foo(long long int& r) {}
foo(i) // Error
foo(reinterpret_cast<long long int&>(i)) // Potential problem
// same as *reinterpret_cast<T*>(&(i))
std::cout << std::cin.rdbuf(); // consume
std::stringstream ss{"TEST"};
std::cin.rdbuf(ss.rdbuf()); // shallow copy
- discussion
sizeof(arr)[0] === sizeof(arr[0])
(whensizeof expression
)
static uint32_t my_arr[2];
static_assert(sizeof(my_arr) == 8, "");
static_assert(sizeof(my_arr[0]) == 4, "");
static_assert(sizeof(my_arr)[0] == 4, "");
static_assert(sizeof(int)*p == p * 4, ""); // will not parse as size((int*)p);
- FBString
Small strings (<= 23 chars) are stored in-situ without memory allocation. (align as 24 bytes data type) Medium strings (24 - 255 chars) are stored in malloc-allocated memory and copied eagerly. Large strings (> 255 chars) are stored in malloc-allocated memory and copied lazily.
class Foo {
struct Bar { int i; };
public:
Bar Baz() { return Bar(); } // Your problem...
};
class A {
class B {};
public:
typedef B user_t; // Because you choose it
};
int main() {
Foo f;
// Foo::Bar b = f.Baz(); // error
auto b = f.Baz(); // ok
std::cout << b.i;
}
#include <type_traits>
using namespace std;
struct A {
A(int v) : a(v) {}
A(A&&) {a = 1;}
// A(const A&) { a = 1; }
private:
A(const A&) = delete;
int a;
};
int main() {
A a{1};
// A b{a};
cout << is_trivially_copyable<A>::value; // 0 user-defined move ctor
cout << __has_trivial_copy(A); // 1
cout << __is_pod(A); // 0
}
- before CWG-1734, trivial = implicit + delete
1++1; // error
1+ +1 // 2
std::vector<X> vec;
vec.push_back(v.front()); // reallocation may happen before construct new element?
// emplace_back -> new space -> new element -> move/copy
- only c90 allows this
#include<stdio.h>
int main() {
int a = sum(4,6);
printf("%d",a);
return 0;
}
int sum(int a,int b) {
return a + b;
}
- The injected class name means that X is declared as a member of X, so that name lookup inside X always finds the current class, not another X that might be declared at the same enclosing scope.
class X {
using T = typename A::A; // However, this fails because A::A implies constructor // though constructor address should not be taken
// and constructor cannot be directly call by A::A(...))
};
X x1;
class X::X x2; // class X::X is equal to X
class X::X::X x3; // ...and so on...
struct X{ void func(); };
struct Y : public X{};
decltype(&Y::func) // void (X::*)()
- so
template <typename T>
class base {
protected:
int x;
};
template <typename T>
class derived : public base<T> {
public:
int f() {
return this->x; // base<T>::x || using base<T>::x
}
};
void f(double adouble) {
int i(int(adouble)); // function declaration int i(int adouble);
}
void f() {
int y[3];
y[[] { return 0; }()] = 1; // error
int i [[cats::meow([[]])]]; // OK
}
[[attr1]] class [[attr2]] c {...} [[attr3]] x [[attr4]], y;
// attr1 applies to variables x and y
// attr2 applies to class c
// attr3 applies to x's and y's types
// attr4 applies to variable x
a = 1, 2, 3; // Evaluated as (a = 1), 2, 3. Comma operator is left to right
if (c > b > a) // (c > b > a) is treated as ((c > b) > a), associativity of '>' is left to right.
a = b = c; // Evaluated as a = (b = c). = operator is right to left
int a;
(a = 1) = 2; // Builtin assignment returns T& lvalue
struct A {
template <typename T>
A(T&& v) {}
};
template <typename T>
struct B {
B(T&& v) {};
};
int main() {
int i = 233;
auto&& v = i; // OK auto -> int&
int&& m = i; // Error
auto p = A{i}; // OK T -> int&
auto q = B<int&>{i}; // OK T -> int&
auto r = B{i}; // Error T -> int
T& & p = ... // Error not such grammar
// difference between class template deduction and template argument deduction
}
namespace std
{
typedef long unsigned int size_t;
}
namespace std __attribute__ ((__visibility__ ("default")))
{
template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
typedef _Tp _Type[_Nm];
};
template<typename _Tp, std::size_t _Nm>
struct array
{
typedef std::__array_traits<_Tp, _Nm> _AT_Type;
typename _AT_Type::_Type _M_elems;
};
}
namespace std __attribute__ ((__visibility__ ("default")))
{
template<size_t _Nw>
struct _Base_bitset
{
typedef unsigned long _WordT;
_WordT _M_w[_Nw];
constexpr _Base_bitset() noexcept
: _M_w() { }
};
template<size_t _Nb>
class bitset
: private _Base_bitset<((_Nb) / (8 * 8) + ((_Nb) % (8 * 8) == 0 ? 0 : 1))>
{
};
}
constexpr std::size_t N = 100000;
std::array<std::bitset<N>, N> elems; // The constexpr makes gcc to expand 100000 * 1563 wordT
int main() {}
#include <variant>
#include <iostream>
using namespace std;
struct A{};
struct B{};
struct C{};
/*
struct D{};
struct E{};
struct F{};
struct G{};
struct H{};
struct I{};
struct J{};
struct K{};
struct L{};
struct M{};
struct N{};
struct O{};
*/
int main() {
using T = variant<A, B, C/*, D, E, F, G, H, I, J, K, L, M, N, O*/>;
T a{A{}};
auto p = std::visit(
[](auto&& a, auto&&... args) -> T { return a; },
a, a, a, a
);
}
- another requirement: visitor of std::visit must be exhausive (some return type and can receive all kinds of input value)
-
- g++ directly use get<0>, the hinting was soooooooooo sabi
variant<T, T>
is ill-formed but without diagnose
template <class C> class X
{
public:
template <class T> void get_as();
template <> void get_as(); // Error! full-specialization only allowed in namespace scope
};
template <class C> template<>
void X<C>::get_as<double>() {} // Error! Explicitly specialized members need their surrounding class templates to be explicitly specialized as well.
template <> template<>
void X<int>::get_as<double>() {} // So why no just overload
std::set<T> val{...};
struct MyClass {
MyClass& operator<<(T& val); // #1
template <typename U>
MyClass& operator<<(U& val); // #2
};
for (auto& i: val) {
MyClass{} << *i; // call #2, i -> const iterator
}
#define PRETTY(x) (std::cout << __PRETTY_FUNCTION__ << " : " << (x) << '\n')
struct D;
struct C {
C() { PRETTY(this);}
C(const C&) { PRETTY(this);}
C(const D&) { PRETTY(this);}
};
struct D {
D() { PRETTY(this);}
operator C() { PRETTY(this); return C();}
};
D d;
C c(d); // conversion or exact match ?
#include <iostream>
#include <vector>
#include <cstdint>
#include <functional>
#include <string>
using namespace std;
size_t hash(string s) {
return hash<string>{}(s); // ambiguous
}
- The order of the elements that are not erased is preserved (this makes it possible to erase individual elements while iterating through the container) (since C++14)
- discussion-and-solution
std::unordered_map<...> m;
auto it = m.begin();
while (it != m.end())
{
if (pred(*it))
it = m.erase(it); // UB before 14
else
++it;
}
What's wrong with this?
#include <iostream>
#include <vector>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <functional>
#include <tuple>
#include <cassert>
#include <variant>
using namespace std;
template <std::size_t I, typename ...Args>
auto tuple_index_impl(size_t n, std::tuple<Args...>& tp) {
// compile-time decide/check return type will not notice runtime branch end conditon
// if you only write one if constexpr without else, still not notice
// only if constexpr { ... } else { ... }
// Also: the return type will deduce differently and failed
if (I >= sizeof...(Args)) {
throw std::out_of_range("Out of bound.");
return {};
} else {
if (n == I) {
return std::get<I>(tp);
}
return tuple_index_impl<I + 1, Args...>(n, tp);
}
}
template <typename ...Args>
auto tuple_index(size_t n, std::tuple<Args...>& tp) {
return tuple_index_impl<0, Args...>(n, tp);
}
int main() {
int i = 2;
tuple<int, long, int> tp{1, 233, 2};
/* Question 2: why variant failed (some type ill-formed)
std::visit(
[](long long&& c) {
std::cout << c << std::endl;
},
tuple_index(i, tp)
);
*/
}
int main() {
fork(); // (1 parent and 1 child)
fork() && fork() || fork(); // 4(both fork) + 4(both fork) + 2(only child do this fork)
fork(); // x2 = 20
}
#include <stdio.h>
int main(void)
{
int* x, *y;
x = &(y); // fail in C++, valid in C
y = &(x);
printf("Hello, world!\n");
return 0;
}
class T {
std::vector<int> data_;
public:
std::vector<int>& items() { return data_; }
// ...
};
{
T thing = f();
for (auto& x : thing.items()) {
// Note: “for (auto& x : f().items())” is WRONG
mutate(&x);
log(x);
}
}
- The wrong is because
auto&& temp = f().items(); // This will not extend lifetime! Causes dangling reference
template <class T>
struct Outer
{
template <class U>
void f();
void bar(Outer outer) {
[outer](){ outer.f<int>(); };
}
};
int main() { }
template<class T, class U = T>
constexpr T exchange(T& obj, U&& new_value) // without constexpr, won't work
{
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
struct S
{
int* p;
int n;
constexpr S(S&& other)
:p{std::exchange(other.p, nullptr)}
,n{std::exchange(other.n, 0)}
{}
constexpr S& operator=(S&& other) {
p = std::exchange(other.p, nullptr); // move p, while leaving nullptr in other.p
n = std::exchange(other.n, 0); // move n, while leaving zero in other.n
return *this;
}
};
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S); // They are seperate
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
struct S : enable_shared_from_this<S>
{
// enable_shared_from_this<T> adds a private weak_ptr<T> instance to T which holds the 'one true reference count' for the instance of T.
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
}
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // also bad.
}
int a=5, b=3;
++(a > b ? a : b);
int a=5;
double b=3;
++( a > b ? a : sb); // Error
- same type lvalue -> lvalue
- otherwise -> do common_type -> rvalue
- proposal
using abominable = void() const volatile &&;
- you cannot decay it! const/volatile decorates
this
void f(double x[volatile], const double y[volatile]);
void f(double * volatile x, const double * volatile y);
void f(double a[restrict static 3][5]); // the value of the actual parameter must be a valid pointer to the first element of an array with at least as many elements as specified by expression:
void fadd(double a[static restrict 10],
const double b[static restrict 10])
{
for (int i = 0; i < 10; i++) { // loop can be unrolled and reordered
if (a[i] < 0.0) break;
a[i] += b[i];
}
}
int main() {
using T = union {
int a;
};
// No valid a!
union {
int b;
}
b = 1; // OK
}
constexpr int incr(int& n) {
return ++n;
}
constexpr int g(int k) {
constexpr int x = incr(k); // error: incr(k) is not a core constant
// expression because lifetime of k
// began outside the expression incr(k)
return x;
}
constexpr int h(int k) {
int x = incr(k); // OK: x is not required to be initialized with a core
// constant expression
return x;
}
template <typename R = int, typename U>
R foo() {
return -0.1;
}
int main() {
foo<long long>(1); // return long long
}
template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
// the above is the same as the following: (same as function default parameter)
template<typename T1 = int, typename T2 = int> class A;
// not same
template<typename T = int> class X;
template<typename T = int> class X {}; // error
myClass->myValue;
//--->
(myClass.operator-> ())->myValue;
#include <type_traits>
int main() {
int x = 1;
int& y = x;
int&& z = static_cast<int&&>(x);
[ ]{ static_assert(std::is_same<decltype( x ), int>::value, "A1"); };
[=]{ static_assert(std::is_same<decltype( x ), int>::value, "A2"); };
[&]{ static_assert(std::is_same<decltype( x ), int>::value, "A3"); };
[ ]{ static_assert(std::is_same<decltype((x)), int&>::value, "B1"); };
[=]{ static_assert(std::is_same<decltype((x)), const int&>::value, "B2"); };
[&]{ static_assert(std::is_same<decltype((x)), int&>::value, "B3"); };
[ ]{ static_assert(std::is_same<decltype( y ), int&>::value, "C1"); };
[=]{ static_assert(std::is_same<decltype( y ), int&>::value, "C2"); };
[&]{ static_assert(std::is_same<decltype( y ), int&>::value, "C3"); };
[ ]{ static_assert(std::is_same<decltype((y)), int&>::value, "D1"); };
[=]{ static_assert(std::is_same<decltype((y)), int&>::value, "D2"); };
[&]{ static_assert(std::is_same<decltype((y)), int&>::value, "D3"); };
[ ]{ static_assert(std::is_same<decltype( z ), int&&>::value, "E1"); };
[=]{ static_assert(std::is_same<decltype( z ), int&&>::value, "E2"); };
[&]{ static_assert(std::is_same<decltype( z ), int&&>::value, "E3"); };
[ ]{ static_assert(std::is_same<decltype((z)), int&>::value, "F1"); };
[=]{ static_assert(std::is_same<decltype((z)), int&>::value, "F2"); };
[&]{ static_assert(std::is_same<decltype((z)), int&>::value, "F3"); };
}