Очень люблю C++, но это скорее уже стокгольмский синдром.
Постоянно нахожу способы стрельнуть себе в ногу.
Post #78
1.01K
Мошенники заставили пенсионерку из Москвы переписать квартиру на Rust
- 😁 85
- 🤡 6
- ❤ 1
- 💯 1
C+ @cpp_durka
class A {
int x;
public:
A(int a) : x(a) {}
class B {
int y;
public:
B(int b) : y(b) {}
void f(A& a, B& b){
b.y = 1;
a.x = 1;
}
};
};
void f(A& a, B& b){
b.y = 1;
a.x = 1;
}
int main() {
auto a = A(1);
auto b = A::B(2);
b.f(a, b);
return 0;
}
friend и вынесем за пределы класса.
class A {
int x;
public:
A(int a) : x(a) {}
class B {
int y;
public:
B(int b) : y(b) {}
friend void f(A& a, B& b);
};
};
void f(A& a, A::B& b){
b.y = 1;
a.x = 1;
}
int main() {
auto a = A(1);
auto b = A::B(2);
f(a, b);
return 0;
}
<source>:15:7: error: 'x' is a private member of 'A'
15 | a.x = 1;
| ^
<source>:2:9: note: implicitly declared private here
2 | int x;
|
inhabiting the class scope, и насколько я понял, вот такая ошибка компиляции - это самовольство компиляторов, а в стандарте про это вроде как ничего внятного сказано не было... Или было, но я, в скудоумии своем, не разобрался... h, an object of type std::hash<Key>.k1 and k2, objects of type Key.k1 == k2 is true, h(k1) == h(k2) is also true.std::hash<Key> is a program-defined specialization, h(k1) will never throw an exception.
const auto x1 = 42;
const auto x2 = 42u;
__builtin_printf("%b\n\n", x1 == x2);
__builtin_printf("%#08zx\n%#08zx\n",
std::hash<int>{}(x1),
std::hash<unsigned>{}(x2));
1
0x00002a
0x00002a
const auto j1 = json{x1};
const auto j2 = json{x2};
__builtin_printf("%b\n\n", j1 == j2);
__builtin_printf("%#08zx\n%#08zx\n",
std::hash<json>{}(j1),
std::hash<json>{}(j2));
1
0x286fec1b50
0x286fec1b91
namespace std // NOLINT(cert-dcl58-cpp)
{
/// @brief hash value for JSON objects
/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)
{
std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
{
return nlohmann::detail::hash(j);
}
};

if (dim == 0) idx_dim = i0;
else if (dim == 1) idx_dim = i1;
else if (dim == 2) idx_dim = i2;
else if (dim == 3) idx_dim = i3;
const struct htp_tensor * src = (idx_dim < src0->ne[dim]) ? src0 : src1;
uint32_t s0 = i0;
uint32_t s1 = i1;
uint32_t s2 = i2;
uint32_t s3 = i3;
if (dim == 0 && src == src1) s0 -= src0->ne[0];
if (dim == 1 && src == src1) s1 -= src0->ne[1];
if (dim == 2 && src == src1) s2 -= src0->ne[2];
if (dim == 3 && src == src1) s3 -= src0->ne[3];
struct Empty1 {};
struct Empty2 {};
struct NonEmpty {
int Value;
};
struct MyClass1
:public NonEmpty
,public Empty1
,public Empty2
{
};
struct MyClass2
:public Empty1
,public NonEmpty
,public Empty2
{
};
struct MyClass3
:public Empty1
,public Empty2
,public NonEmpty
{
};
static_assert(sizeof(NonEmpty) + sizeof(Empty1) + sizeof(Empty2) == 6);
static_assert(sizeof(int) == 4);
static_assert(sizeof(MyClass1) == 4);
static_assert(sizeof(MyClass2) == 4);
static_assert(sizeof(MyClass3) == 4);
int main() {}struct Empty {
void zero() { std::memset(this, 0, sizeof(*this)); }
};struct CompressedPair : Empty {
CompressedPair(Empty e, bool x) : Empty(e), b(x) { }
bool b;
};
int main() {
auto a = CompressedPair({}, true);
__builtin_printf("%b\n", a.b);
a.zero();
__builtin_printf("%b\n", a.b);
return 0;
}1
0
no_unique_address.#include <iostream>
template <typename T>
struct optional {
union {
char foo;
T val;
};
[[no_unique_address]] bool engaged;
};
template <typename View>
struct reverse_view {
[[no_unique_address]] optional<typename View::iterator> cache;
View base_;
};
struct MyView {
using iterator = int*;
char begin_;
};
int main() {
std::cout << "sizeof = " << sizeof(reverse_view<MyView>) << std::endl; // clang - 24, gcc - 16
std::cout << "alignof = " << alignof(reverse_view<MyView>) << std::endl;
}
#include <utility>
template <std::size_t N, std::size_t M>
concept C = requires {
[]<std::size_t... Is>(std::index_sequence<Is...>)
requires requires {
[]<std::size_t... Js>(std::index_sequence<Js...>)
requires requires { true; }
{}(std::make_index_sequence<M>{});
}
{}(std::make_index_sequence<N>{});
};
static_assert(C<2, 3>);
struct S {
int x;
auto foo() {
return [*this](this auto&& self) {
__builtin_printf("%d ", x);
x = 10;
};
}
};
foo капчурит this, и потом модифицирует значение. Но что будет, если мы сделаем лямбду константной?
int main() {
S s{ 5 };
const auto l = s.foo();
l();
s.x = 15;
l();
__builtin_printf("%d\n", s.x);
}

int main() {
std::map<int, std::unique_ptr<int>> m;
m.emplace(1, std::make_unique<int>(42));
auto p = std::make_unique<int>(100);
auto [it, inserted] = m.emplace(std::make_pair(1, std::move(p)));
}
Return value
A pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool value set to true if and only if the insertion took place.
int main() {
std::map<int, std::unique_ptr<int>> m;
m.emplace(1, std::make_unique<int>(42));
auto p = std::make_unique<int>(100);
auto [it, inserted] = m.emplace(std::make_pair(1, std::move(p)));
std::cout << std::boolalpha;
std::cout << "inserted = " << inserted << '\n';
std::cout << "p is null = " << (p == nullptr) << '\n';
std::cout << "*it->second = " << *it->second << '\n';
}
inserted = false
p is null = true
*it->second = 42
int main() {
auto p = std::make_unique<int>(100);
std::move(p);
std::cout << std::boolalpha;
std::cout << "p is null = " << (p.get() == nullptr) << '\n';
}
p is null = false
make_pair, но это в тестовом примере, где это легко заметить. И когда уже знаешь проблему.
decltype(auto) f(int&& x)
{
return (x);
}
static_assert(std::same_as<decltype(f(1)), int&>);
template<typename T>
struct S {
S(T) {
std::cout << "S(T)" << std::endl;
}
template<typename Iter>
S(Iter beg, Iter end) {
(beg); (end);
std::cout << "S(Iter beg, Iter end)" << std::endl;
}
};
int a = 3;
auto s1 = S(a);
static_assert(std::same_as<decltype(s1),
S<int>>
);
std::vector<double> v;
auto s2 = S(v.begin(), v.end());
error: no viable constructor or deduction guide for deduction of template arguments of 'S'
template<typename Iter>
S(Iter, Iter)
-> S<typename std::iterator_traits<Iter>::value_type>;
std::vector<double> v;
auto s2 = S(v.begin(), v.end());
static_assert(std::same_as<decltype(s2),
S<double>>
);
#include <concepts>
struct Charmander {};
struct Charmeleon {};
struct Charizard {};
template<typename Form>
struct Pokemon {
Pokemon(Form) {}
template<typename OtherForm>
Pokemon(const Pokemon<OtherForm>&) {}
};
Pokemon(const Pokemon<Charmander>&)
-> Pokemon<Charmeleon>;
Pokemon(const Pokemon<Charmeleon>&)
-> Pokemon<Charizard>;
Pokemon(const Pokemon<Charizard>&)
-> Pokemon<Charmander>;
int main() {
Pokemon p1 = Charmander{};
Pokemon p2 = p1;
Pokemon p3 = p2;
Pokemon p4 = p3;
static_assert(std::same_as<
decltype(p1),
Pokemon<Charmander>
>);
static_assert(std::same_as<
decltype(p2),
Pokemon<Charmeleon>
>);
static_assert(std::same_as<
decltype(p3),
Pokemon<Charizard>
>);
static_assert(std::same_as<
decltype(p4),
Pokemon<Charmander>
>);
}
int main() {
Box b;
b->hi();
}
Box, иииии... вызываем у него оператор ->, а у того, что он вернет вызывает функцию hi(). Логично? Логично! Box.
struct Box {
Proxy operator->() {
return {};
}
};
hi(). Логично? Логично. Proxy.
struct Proxy {
Leaf* operator->() {
static Leaf x;
return &x;
}
};
hi() нет. Ошибка компиляции. Логично? А хрен там плавал. ok
#include <iostream>
struct Leaf {
void hi() { std::cout << "ok"; }
};
struct Proxy {
Leaf* operator->() {
static Leaf x;
return &x;
}
};
struct Box {
Proxy operator->() {
return {};
}
};
int main() {
Box b;
b->hi();
}
-> до самого конца. Например сработает даже вот это:
#include <iostream>
struct Leaf {
void hi() { std::cout << "ok"; }
};
struct Proxy4 {
Leaf* operator->() {
static Leaf x;
return &x;
}
};
struct Proxy3 {
Proxy4 operator->() {
return {};
}
};
struct Proxy2 {
Proxy3 operator->() {
return {};
}
};
struct Proxy {
Proxy2 operator->() {
return {};
}
};
struct Box {
Proxy operator->() {
return {};
}
};
int main() {
Box b;
b->hi();
}
const std::vector<Obj>& vec =
(argc > 1)
? std::vector<Obj>{}
: ready_vec;
const продлевает время жизни ссылки.
#include <iostream>
#include <vector>
struct Obj {
explicit Obj() {}
Obj(const Obj&) {
std::cout << "copy\n";
}
};
int main(int argc) {
auto ready_vec = std::vector<Obj>{};
ready_vec.reserve(1);
ready_vec.emplace_back();
const std::vector<Obj>& vec =
(argc > 1)
? std::vector<Obj>{}
: ready_vec;
return vec.size();
}
#include <iostream>
#include <vector>
struct Obj {
explicit Obj() {}
Obj(const Obj&) {
std::cout << "copy\n";
}
};
int main(int argc) {
auto ready_vec = std::vector<Obj>{};
ready_vec.reserve(1);
ready_vec.emplace_back();
const auto empty_vec = std::vector<Obj>{};
const std::vector<Obj>& vec =
(argc > 1)
? empty_vec
: ready_vec;
return vec.size();
}
const std::vector<Obj>& vec =
(argc > 1)
? std::vector<Obj>{}
: ready_vec;
int fn(int a, int b) {
int res = 0;
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
if (a > b) { // проблемный бренч
res = std::rand();
}
res = std::max(res, a);
return res;
}
#include <iostream>
int main() {
bool b = true;
b++;
std::cout << b;
}
<source>:6:5: error: use of an operand of type 'bool' in 'operator++' is forbidden in C++17
%:include <iostream>
int main() <%
int a<:3:> = <% 10, 20, 30 %>;
std::cout << a<:1:>;
%>
#include <memory>
#include <iostream>
namespace user {
struct user_type {};
using my_type = std::shared_ptr<user_type>;
void tie(my_type const&, my_type const&)
{
std::cout << "user::tie\n";
}
void oups()
{
my_type t1;
my_type t2;
tie(t1, t2);
}
} // namespace user
int main()
{
user::oups();
}
Program returned: 0
Program returned: 0
user::tie
Program returned: 3221225595
void oups()
{
my_type t1;
my_type t2;
(tie)(t1, t2);
}
cpp
#include <iostream>
#include <vector>
template <class T>
struct LoggedVector : std::vector<T> {
void Dump() const {
if (empty()) {
std::cout << "empty\n";
return;
}
std::cout << "size = " << size() << '\n';
}
};
int main() {
LoggedVector<int> v;
v.Dump();
}
void Dump() const {
if (this->empty()) {
std::cout << "empty\n";
return;
}
std::cout << "size = " << this->size() << '\n';
}
using std::vector<T>::size;
using std::vector<T>::empty;
template <auto T = []{}>
struct S {};
S a; S b;
T - это лямбда. И если мы таким образом определяем переменные, то в тип S записываются разные лямбды, и у нас получаются два разных типа:
static_assert(
!std::is_same_v<decltype(a), decltype (b)>
);
S<> a, b;
static_assert(
std::is_same_v<decltype(a), decltype (b)>
);
S a, b;