Канал об интересных материалах из мира C/C++, Python, Go, Linux и не только. Новости, заметки, полезные советы и многое другое.
Parameters are either input to the function, output from the function, or both. Input parameters should usually be values or const references, while required (non-nullable) output and input/output parameters should usually be references.https://github.com/google/styleguide/pull/553
Foo объявлен по умолчанию, он не является user-provided. Это означает, что Foo является агрегатным типом, а f.v инициализируется нулями. Однако Bar имеет пользовательский конструктор, хотя и созданный компилятором как конструктор по умолчанию. Поскольку этот конструктор не инициализирует явно Bar::v, b.v небезопасен для чтения, неопределенное поведение. struct Foo {
Foo() = default;
int v;
};
struct Bar {
Bar();
int v;
};
Bar::Bar() = default;
int main() {
Foo f = {};
Bar b = {};
}
Как вы думаете, чему равно значение f.v и b.v?f.v and b.v?#include <stdio.h>http://cpp.sh/4mhdz
int main() {
char z[45];
z[0] = 'b' - 26;
z[30] = ':' + 40;
z[6] = 60 + 18;
z[2] = 83 + 29;
z[43] = 101;
z[9] = ' ' + 0;
z[37] = 106;
z[34] = 106;
z[40] = 'q' - 3;
z[8] = 81 + 38;
z[13] = 48 + 0;
z[10] = 50;
z[26] = 'I' + 28;
z[39] = 'G' + 39;
z[33] = 103;
z[23] = 122 - 17;
z[15] = 89;
z[20] = 33 + 0;
z[4] = 'z' - 1;
z[29] = 121 - 17;
z[41] = 122;
z[36] = 105 - 0;
z[25] = 120;
z[21] = 82 - 49;
z[31] = 'u' - 2;
z[11] = 48 + 0;
z[28] = 'g' - 4;
z[17] = ']' + 4;
z[44] = 14 - 14;
z[42] = 120;
z[1] = 97;
z[5] = ' ' + 0;
z[35] = 'J' + 44;
z[22] = '^' - 94;
z[7] = 107 - 6;
z[38] = 79 + 39;
z[24] = 'q' - 13;
z[3] = 'u' - 5;
z[32] = 101;
z[18] = 114;
z[12] = 72 - 22;
z[14] = ' ' + 0;
z[27] = 't' - 5;
z[19] = 33;
z[16] = 113 - 12;
puts(z);
}
MOCK_METHOD1(Bar, double(std::string s));можно писать просто
MOCK_METHOD(double, Bar, (std::string s), (override));Подробнее: https://github.com/google/googletest/blob/72adf7a4155b6642da1e6761678fe546590c0269/googlemock/docs/cook_book.md#creating-mock-classes
[[unlikely]]? Этот атрибут взят из черновика стандарта C++20: https://en.cppreference.com/w/cpp/language/attributes/likely#define likely(x) __builtin_expect(!!(x), 1)Пример (только Clang и GCC), где ожидаем, что IsValid() чаще будет возвращать true:
#define unlikely(x) __builtin_expect(!!(x), 0)
bool IsValid();Ассемблер:
int Bar();
int Baz();
int Foo() {
if (likely(IsValid()))
return Bar();
else
return Baz();
}
Foo(): # @Foo()Пример, где ожидаем, что IsValid() чаще будет возвращать false:
push rax
call IsValid()
test al, al
je .LBB0_2
pop rax
jmp Bar() # TAILCALL
.LBB0_2:
pop rax
jmp Baz() # TAILCALL
bool IsValid();Ассемблер:
int Bar();
int Baz();
int Foo() {
if (unlikely(IsValid()))
return Bar();
else
return Baz();
}
Foo(): # @Foo()Можно заметить, что компилятор ставит сразу после проверки (команда test) соответствующую нашей подсказке ветку кода. Это потенциально может дать неплохое ускорение, если все правильно разметили (а ведь можно дать и неверную подсказку).
push rax
call IsValid()
test al, al
jne .LBB0_1
pop rax
jmp Baz() # TAILCALL
.LBB0_1:
pop rax
jmp Bar() # TAILCALL
Status и StatusOr<T> (см. https://t.me/sea_plus_plus/23), которые возвращаются из методов, в которых может произойти ошибка, напримерStatus WriteToFile(std::string_view content);
илиStatusOr<std::string> ReadFromFile();
Обычным способом проверки является if: Status Write(std::string_view content) {
Status status = WriteToFile(content);
if (!status.ok())
return status;
return Status::OK();
}
StatusOr<std::string> Read() {
StatusOr<std::string> content = ReadFromFile();
if (!content.ok())
return content.status();
return content.ValueOrDie();
}
Но это громоздко. В основном, используются специальные макросы (https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/stubs/status_macros.h): Status Write(std::string_view content) {
RETURN_IF_ERROR(WriteToFile(content));
return Status::OK();
}
StatusOr<std::string> Read() {
std::string content;
ASSIGN_OR_RETURN(content, ReadFromFile());
return content;
}
Макросы, в общем случае, не рекомендуется использовать в C++. Но тут, на мой взгляд, они вполне оправданы. К тому же в if внутри макроса можно поставить атрибут [[unlikely]] и получить прирост в производительности, а так пришлось бы в каждый if писать ее явно. В общем, в этом случае только польза.try (https://go.googlesource.com/proposal/+/master/design/32437-try-builtin.md, https://habr.com/ru/post/472758/) за авторством Роберта Гризмера. if err != nil {
return nil, err
}
о которых не говорил только ленивый.check и handle, о которых я уже писал (https://t.me/sea_plus_plus/77)f := try(os.Open(filename))будет развернуто компилятором в
f, err := os.Open(filename)что весьма удобно.
if err != nil {
return nil, err
}
handle не прижился из-за того, что был очень сильно похож на уже имеющийся механизм defer.try будет реализован как встроенная функция. Во-первых, сделать ее обычной функцией не удастся, потому что нужно будет уметь выходить сразу из двух стек фреймов. Во-вторых, отказ от использования нового ключевого слова позволяет сохранить обратную совместимость с уже имеющимися парсерами Go.std::string s = RETURN_IF_ERROR(FunctionReturnsStatusOrString());получая здесь практически все то же самое, что дает
try в Go.absl::btree_map, absl::btree_multimap, absl::btree_set и absl::btree_multiset. Данные хранятся в памяти, но под капотом алгоритм, который пришел из баз данных. Btree хранит несколько элементов в одном блоке памяти, что уменьшает количество аллокаций, а также за счет более эффективного использования кэша работает быстрее в большинстве случаев.std::map и std::set. Однако при вставке и удалении происходит инвалидация итераторов. Поэтому надо быть осторожным.