Немного про новые стандарты: когда я показываю какую-то упячку, часто мне говорят "просто не пользуйся вот этим...".
Например, "не пользуйся new/delete, пользуйся умными указателями".
Или "не пользуйся конструкторами с круглыми скобками, пользуйся {}. И порядок зафиксирует и от кучи проблем избавит.".
Увы, новые конструкции просто создают другое подмножество проблем.
Вот к примеру, что выведет вот такой код?
Правильно, он выведет
Что характерно, вот такая строчка
Не скомпилируется. Потому что у строки нет конструктора от char. Точнее есть, но там надо развлекаться веселее.
Это будет
Так или иначе, но у нас новые конструкции просто создают новые проблемы.
Ну и самая большая проблема, когда эти вещи вызываются неявно.
Человек, который прислал мне похожий пример, утверждает, что он пришел из продакшн кода.
Что тут происходит? double кастится к int, int кастится к char, char кастится к string, в мапу записывается буква
Например, "не пользуйся new/delete, пользуйся умными указателями".
Или "не пользуйся конструкторами с круглыми скобками, пользуйся {}. И порядок зафиксирует и от кучи проблем избавит.".
Увы, новые конструкции просто создают другое подмножество проблем.
Вот к примеру, что выведет вот такой код?
#include <iostream>
int main() {
auto a = std::string{48};
std::cout << a << std::endl;
}
Правильно, он выведет
0. Потому что идет неявное преобразование инта к чару. И chr(48) == '0'.Что характерно, вот такая строчка
auto b = std::string(48); // error: no matching function for call to
Не скомпилируется. Потому что у строки нет конструктора от char. Точнее есть, но там надо развлекаться веселее.
#include <iostream>
int main() {
auto b = std::string(48, 48.0);
std::cout << b << std::endl;
}
Это будет
000000000000000000000000000000000000000000000000
Так или иначе, но у нас новые конструкции просто создают новые проблемы.
Ну и самая большая проблема, когда эти вещи вызываются неявно.
Человек, который прислал мне похожий пример, утверждает, что он пришел из продакшн кода.
#include <iostream>
#include <unordered_map>
int main() {
std::unordered_map<std::string, std::string> m;
double i = 65.5;
m["hello"] = i;
std::cout << m.at("hello");
return 0;
}
Что тут происходит? double кастится к int, int кастится к char, char кастится к string, в мапу записывается буква
A. Прекрасно!godbolt.org
Compiler Explorer - C++
int main() {
auto a = std::string{48};
std::cout << a << std::endl;
// auto b = std::string(48); // error: no matching function for call to
}
auto a = std::string{48};
std::cout << a << std::endl;
// auto b = std::string(48); // error: no matching function for call to
}
🥴14🔥3😢3❤2🤩1
Ну а как еще можно было назвать канал, как не "Дурка"?
Ну сам факт, что вот такой код компилируется, запускается, а еще и не падает, и выводит 0!!!
Нет, конечно, понятно даже почему, если разобрать поднаготную устройства функций-членов класса. Но блииииин........
Ну сам факт, что вот такой код компилируется, запускается, а еще и не падает, и выводит 0!!!
#include <iostream>
class Test {
public:
void test() {
std::cout << this << std::endl;
}
};
Test & create()
{
return *((Test*)NULL);
}
int main()
{
Test &t = create();
t.test();
}
Нет, конечно, понятно даже почему, если разобрать поднаготную устройства функций-членов класса. Но блииииин........
godbolt.org
Compiler Explorer - C++
class Test {
public:
void test() {
std::cout << this << std::endl;
}
};
Test & create()
{
return *((Test*)NULL);
}
int main()
{
Test &t = create();
t.test();
}
public:
void test() {
std::cout << this << std::endl;
}
};
Test & create()
{
return *((Test*)NULL);
}
int main()
{
Test &t = create();
t.test();
}
💊9😨3❤1👍1👎1😢1
Еще одна абсолютно бесполезная, но унаследнованная штука в С++. Заголовок может заинклюдить сам себя.
Например вот такой заголовок:
Сработает совершенно нормально.
Ни ворнингов, ничего. А самое главное - как бы так передефайнить разные куски, чтобы на этом механизме устроить перебор? Ну, перебрать все комбинации из 5 дефайнов, и для каждого определить функцию? Было бы... Забавно?...
Например вот такой заголовок:
// megaheader.hpp
#ifndef MEGAHEADER_HPP
int foo() {
return 1;
}
#define MEGAHEADER_HPP
#include "megaheader.hpp"
#else
int bar() {
return 2;
}
#endif
Сработает совершенно нормально.
#include <iostream>
#include "megaheader.hpp"
int main() {
std::cout << "foo: " << foo() << std::endl;
std::cout << "bar: " << bar() << std::endl;
return 0;
}
foo: 1
bar: 2
Ни ворнингов, ничего. А самое главное - как бы так передефайнить разные куски, чтобы на этом механизме устроить перебор? Ну, перебрать все комбинации из 5 дефайнов, и для каждого определить функцию? Было бы... Забавно?...
🤔4😁2🥴2❤1👍1
Причины, по которым этот канал называется "дурка".
Вот такой вот метод:
Компиляторы сожрут. И не выведут ни одного ворнинга при Wall.
Вот полный пример.
У меня все, увидимся в дурдоме
Вот такой вот метод:
void Suicide() {
delete this;
}
Компиляторы сожрут. И не выведут ни одного ворнинга при Wall.
Вот полный пример.
#include <iostream>
#include <vector>
struct S {
int a = 13;
std::vector<int> v{};
void Suicide() {
delete this;
}
};
int main(){
auto* s = new S();
s->Suicide();
return 0;
}
У меня все, увидимся в дурдоме
godbolt.org
Compiler Explorer - C++
struct S {
int a = 13;
std::vector<int> v{};
void Suicide() {
delete this;
}
};
int main(){
auto* s = new S();
s->Suicide();
return 0;
}
int a = 13;
std::vector<int> v{};
void Suicide() {
delete this;
}
};
int main(){
auto* s = new S();
s->Suicide();
return 0;
}
🤡10🤝5💊3🤷♂2❤1
Ну, к разного рода наркомании, что
Это одно и то же - все уже привыкли.
А как насчет инициализации массива с указанием ренжа индексов?
Типа:
Или инициализации единицы для вайтспейстов:
Это нормально без дополнительных выкрутасов работает на clang, правда, грустит и плюется ворнингами.
Эта штука называется Designated Initializers, и является частью ISO C99, а С++ пытается быть совместимым с С90, а 99 - оно так, по желанию.
На годболте у меня так и не получилось скомпилировать вот такой пример на gcc:
однако локально он нормально компилируется вот такой командной строкой:
Вот какие-то такие развлечения....
std::cout << a[42] << std::endl;
std::cout << 42[a] << std::endl;
Это одно и то же - все уже привыкли.
А как насчет инициализации массива с указанием ренжа индексов?
Типа:
static constexpr int a[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
Или инициализации единицы для вайтспейстов:
int whitespace[256] =
{
[' '] = 1, ['\t'] = 1,
['\f'] = 1, ['\n'] = 1,
['\r'] = 1
};
Это нормально без дополнительных выкрутасов работает на clang, правда, грустит и плюется ворнингами.
Эта штука называется Designated Initializers, и является частью ISO C99, а С++ пытается быть совместимым с С90, а 99 - оно так, по желанию.
На годболте у меня так и не получилось скомпилировать вот такой пример на gcc:
#include <iostream>
int main() {
static constexpr int a[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
std::cout << a[42] << std::endl;
std::cout << 42[a] << std::endl;
int whitespace[256] =
{
[' '] = 1, ['\t'] = 1,
['\f'] = 1, ['\n'] = 1,
['\r'] = 1
};
return 0;
}
однако локально он нормально компилируется вот такой командной строкой:
g++ --std=gnu++23 main.cpp
Вот какие-то такие развлечения....
godbolt.org
Compiler Explorer - C++
int main() {
static constexpr int a[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
std::cout << a[42] << std::endl;
std::cout << 42[a] << std::endl;
int whitespace[256] =
{
[' '] = 1, ['\t'] = 1,
['\f'] = 1, ['\n']…
static constexpr int a[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
std::cout << a[42] << std::endl;
std::cout << 42[a] << std::endl;
int whitespace[256] =
{
[' '] = 1, ['\t'] = 1,
['\f'] = 1, ['\n']…
👏11😁9🥴6❤1
Есть задачка, абсолютно практическая на самом деле.
Все знают про padding:
Размер структуры Foo 16, а у Bar, несмотря на то, что переменные тех же типов, размер 24. Потому что double размера 8 должен быть помещен по адресу памяти, кратному размеру самой переменной, поэтому место между переменными заполняется 8 фейковыми байтами.
Это классика, но вопрос не про это. Утверждение:
Задача - доказать что утверждение верно или привести контрпример.
Как я сказал, задача на самом деле практическая, подробности в следующем посте.
Все знают про padding:
#include <iostream>
struct Foo {
char a; // 1
int b; // 4
double c; // 8
};
struct Bar {
char a; // 1
double c; // 8
int b; // 4
};
int main() {
std::cout << sizeof(Foo) << '\n'; // 16
std::cout << sizeof(Bar) << '\n'; // 24
}
Размер структуры Foo 16, а у Bar, несмотря на то, что переменные тех же типов, размер 24. Потому что double размера 8 должен быть помещен по адресу памяти, кратному размеру самой переменной, поэтому место между переменными заполняется 8 фейковыми байтами.
Это классика, но вопрос не про это. Утверждение:
Размер структуры невозможно поменять, переставив переменные в ней В ОБРАТНОМ ПОРЯДКЕ
Задача - доказать что утверждение верно или привести контрпример.
Как я сказал, задача на самом деле практическая, подробности в следующем посте.
Если вы очень любите питон, у меня для вас выход:
P.S. тот, кто мне скинул этот код, ссылался на реальную лабораторную студента...
🤡🤡🤡🤡🤡🤡🤡🤡
#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <string>
#define print(data) cout<<data<<endl;
#define ord(data) int(data[0])
#define str(data) char(data)
#define open fopen
#define write(f, data) fputc(data, f)
using namespace std;
string input()
{
string s;
cin>>s;
return s;
};
FILE* f;
int chr;
int main() {
print("Enter:");
f = open("code.txt", "w");
chr = ord(input());
print(chr);
write(f, str(chr));
return 0;
}
P.S. тот, кто мне скинул этот код, ссылался на реальную лабораторную студента...
🤡🤡🤡🤡🤡🤡🤡🤡
💊18😍4🏆4👍2🔥2😐1🙈1
А вот в C23 можно тип объявлять прямо в return типе.
А вот в С++ такого нельзя:
Товарищи из комитета, отстаете. Где эти безусловно нужные всем языковые фичи?
#include <stdio.h>
struct{int a; float b;} test()
{
return (typeof(test())){1337, 666.666};
}
int main()
{
auto a = test();
printf("%d %f\n", a.a, a.b);
return 0;
}
А вот в С++ такого нельзя:
error: new types may not be defined in a return type
Товарищи из комитета, отстаете. Где эти безусловно нужные всем языковые фичи?
godbolt.org
Compiler Explorer - C (x86-64 clang (trunk))
struct{int a; float b;} test()
{
return (typeof(test())){1337, 666.666};
}
int main()
{
auto a = test();
printf("%d %f\n", a.a, a.b);
return 0;
}
{
return (typeof(test())){1337, 666.666};
}
int main()
{
auto a = test();
printf("%d %f\n", a.a, a.b);
return 0;
}
💊17🤣6🫡5🔥1
Сегодня у нас поиски глубинного смысла в С++ на основе примеров, которые подсказывают подписчики.
В чем цимес. Лично мне в С++ не всегда понятно, что должно быть "нормальным поведением по-умолчанию", а что "нужно прописать явно".
Давайте посмотрим вот сюда:
Мы создали структуру, прописали, что ее можно сравнивать (по дефолту), и сравнили.
А теперь давайте унаследуем такую же структуру от пустой структуры
Мы не можем скомпилировать этот код, потому что оператор сравнения "по-умолчанию" не создается, пока мы не объявим явным образом оператор сравнения для пустой структуры.
Другими словами, нам надо явно писать что-то такое:
И вот не понятно, толи все логично, и я придераюсь. Толи правда неплохо бы генерировать операторы сравнения для пустых структур, а явно прописовать требовать только когда мы хотим их явно запретить. Я не знаю, я не понимаю...
В чем цимес. Лично мне в С++ не всегда понятно, что должно быть "нормальным поведением по-умолчанию", а что "нужно прописать явно".
Давайте посмотрим вот сюда:
#include <iostream>
struct Good {
int i;
bool operator==(const Good&) const = default;
};
int main() {
Good good1{1}, good2{2};
std::cout << (good1 == good2) << std::endl;
return 0;
}
Мы создали структуру, прописали, что ее можно сравнивать (по дефолту), и сравнили.
А теперь давайте унаследуем такую же структуру от пустой структуры
#include <iostream>
struct Empty {
// bool operator==(const Empty&) const = default;
};
struct Bad : Empty {
int i;
bool operator==(const Bad&) const = default;
/* error:
constexpr bool Bad::operator==(const Bad&) const'
is implicitly deleted because the default
definition would be ill-formed
*/
};
static_assert(sizeof(Good) == sizeof(Bad));
int main() {
Bad bad1{{}, 1}, bad2{{}, 2};
std::cout << (bad1 == bad2) << std::endl;
return 0;
}
Мы не можем скомпилировать этот код, потому что оператор сравнения "по-умолчанию" не создается, пока мы не объявим явным образом оператор сравнения для пустой структуры.
Другими словами, нам надо явно писать что-то такое:
struct Empty {
bool operator==(const Empty&) const {
return true;
}
};
И вот не понятно, толи все логично, и я придераюсь. Толи правда неплохо бы генерировать операторы сравнения для пустых структур, а явно прописовать требовать только когда мы хотим их явно запретить. Я не знаю, я не понимаю...
godbolt.org
Compiler Explorer - C++
struct Good {
int i;
bool operator==(const Good&) const = default;
};
struct Empty {
// bool operator==(const Empty&) const = default;
};
struct Bad : Empty {
int i;
bool operator==(const Bad&) const = default;
};
static_assert(sizeof(Good)…
int i;
bool operator==(const Good&) const = default;
};
struct Empty {
// bool operator==(const Empty&) const = default;
};
struct Bad : Empty {
int i;
bool operator==(const Bad&) const = default;
};
static_assert(sizeof(Good)…
🤔7😁3❤2
И снова спасибо подписчикам за отборный контент.
Оказывается, в С++ можно объявить оператор каста к... void.
Да, он ворнингом скажет, что ты никогда не сможешь его использовать, но объявить, и даже скомпилировать - это запросто.
Но самая большая радость... При должном желании и упорстве, вопреки предупреждениям ворнинга, вы таки сможете это запустить:
И вот это уже вообще взрыв мозга! 🤯
Оказывается, в С++ можно объявить оператор каста к... void.
struct X {
// warning: Conversion function converting 'X' to 'void' will never be used
operator void() { std::cerr << "void\n"; }
};
Да, он ворнингом скажет, что ты никогда не сможешь его использовать, но объявить, и даже скомпилировать - это запросто.
Но самая большая радость... При должном желании и упорстве, вопреки предупреждениям ворнинга, вы таки сможете это запустить:
#include <iostream>
struct X {
// warning: Conversion function converting 'X' to 'void' will never be used
operator void() { std::cerr << "void\n"; }
};
int main(int argc, char *argv[]) {
X x;
(void)x; // no
static_cast<void>(x); // no
x.operator void(); // YES!!!
return 0;
}
И вот это уже вообще взрыв мозга! 🤯
godbolt.org
Compiler Explorer - C++
struct X {
// warning: Conversion function converting 'X' to 'void' will never be used
operator void() { std::cerr << "void\n"; }
};
int main(int argc, char *argv[]) {
X x;
(void)x; // no
static_cast<void>(x); // no
x.operator void();…
// warning: Conversion function converting 'X' to 'void' will never be used
operator void() { std::cerr << "void\n"; }
};
int main(int argc, char *argv[]) {
X x;
(void)x; // no
static_cast<void>(x); // no
x.operator void();…
😁21💊11🔥1