Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

256bit(32バイト)にアラインされたメモリを取得する(C++17)

C++17以上ではnewでアラインされたメモリを簡単に取得できるようになりました。

    for (int i = 0; i < 10000; ++i) {
        float* a = new (std::align_val_t{32}) float;
        
        auto val = reinterpret_cast<size_t>(a);
        if ((val % 32) != 0) {
            assert(false);
        }
    }

余談ですが64bit環境のWindows VC++ではnewは16バイト、 macOS10.6以上の64bit環境のmallocでは16バイトにアラインされるようです。

https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/reference/malloc?view=msvc-170

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Cocoa64BitGuide/OptimizingMemoryPerformance/OptimizingMemoryPerformance.html

C++20で気になる機能

  • 2 つの値の中点を計算する関数が追加された。
  • 浮動小数点型のatomic操作関数が追加された。
  • 円周率の定義が使えるようになった。
  • コンテナにcontains()関数が追加。
  • 2 の累乗数に関する関数が追加された。
  • std::make_shared()に配列サポートが追加された。
  • 定数式での仮想関数呼び出しが可能.
struct IValue {
    virtual int value() const = 0;
};

struct Value : public IValue {
    constexpr int value() const override {
        return 2022;
    }
};


int main(int argc, const char * argv[]) {

    constexpr Value v;
    static_assert(v.value() == 2022);
}
  • std::format
  • コンセプト swiftのprotocol whereに近いことができるようになった。
  • ranges (今の所MSVCのみ対応,clang,GCC非対応)

C++17のstd::optional

C++17のstd::optionalはよいので積極的に使っていきましょう。

std::optional

swiftのoptionalと同じようなことができるようになりました。

#include <string>
#include <optional>

struct Data {
    int x;
    int y;
    Data(int x, int y)
        : x(x)
        , y(y)
    {
    }
};

std::optional<Data> makeData(int x, int y)
{
    if (y == 0) {
        return std::nullopt;
    }
    
    return std::make_optional<Data>(x, y);
}

int main(int argc, const char * argv[]) {
    const auto data = makeData(1000, 1);
    if (data) {
        printf("%d %d\n", data->x, data->y);
    }
    
    return 0;
}

C++ 無名ラムダ式の即時実行

C++無名ラムダ式の即時実行の仕方です。
無名ラムダ式定義し、即時実行しています。
無名ラムダの場合は引数部分を定義する必要はなく、キャプチャする変数の指定だけが必要です。

const std::vector<int> list = { 1, 2, 3 };
const auto sum = [&]() {
    return list[0] + list[1] + list[2];
}();
printf("%d\n", sum);

[&]の部分が全ての変数、ここではlistのみですが、を参照キャプチャすることを表しています。
[&]を[]にすると以下のようにコンパイルエラーとなります。
メッセージを見る限り暗黙的にキャプチャできる場合は省略できそうです。

/Users/pebble8888/develop/a/a/main.cpp:13:16: error: variable 'list' cannot be implicitly captured in a lambda with no capture-default specified
        return list[0] + list[1] + list[2];
               ^
/Users/pebble8888/develop/a/a/main.cpp:11:28: note: 'list' declared here
    const std::vector<int> list = { 1, 2, 3 };
                           ^
/Users/pebble8888/develop/a/a/main.cpp:12:22: note: lambda expression begins here
    const auto sum = []() -> int {
                     ^
/Users/pebble8888/develop/a/a/main.cpp:13:26: error: variable 'list' cannot be implicitly captured in a lambda with no capture-default specified
        return list[0] + list[1] + list[2];
                         ^
/Users/pebble8888/develop/a/a/main.cpp:11:28: note: 'list' declared here
    const std::vector<int> list = { 1, 2, 3 };
                           ^
/Users/pebble8888/develop/a/a/main.cpp:12:22: note: lambda expression begins here
    const auto sum = []() -> int {
                     ^
/Users/pebble8888/develop/a/a/main.cpp:13:36: error: variable 'list' cannot be implicitly captured in a lambda with no capture-default specified
        return list[0] + list[1] + list[2];
                                   ^
/Users/pebble8888/develop/a/a/main.cpp:11:28: note: 'list' declared here
    const std::vector<int> list = { 1, 2, 3 };
                           ^
/Users/pebble8888/develop/a/a/main.cpp:12:22: note: lambda expression begins here
    const auto sum = []() -> int {
                     ^
3 errors generated.

なお、ここではラムダ式の戻り値定義を省略しています。

ラムダ式の即時実行を行うメリットは戻り値の変数をconstにできることです。
このコード以後変更されないことが明確になります。

私がラムダ式を使いたい時はほとんど無名ラムダ式です。
名前付きラムダ式を使いたい場合は関数にした方が見通しが良いので全く使いません。
  

C++14 共有ロック

C++14 で共有ロックというものが実装された。
これは1つのリソースに対して、1つのスレッドが書き込みを行い、2つ以上のスレッドが同時に読み込みを行うケースで用いる。
2つの読み込みスレッドは一つのstd::shard_mutexに対して、lock_shared()(共有ロック)を呼び出すと読み込み中であることを宣言することにより、書き込みスレッドが呼び出すlock()(排他ロック)をブロックすることができる。
2つの読み込みスレッドのlock_shared()同士は互いにブロックすることはない。

書き込みスレッドが1つ、読み込みスレッドが1つの場合は利用することに意味はない。