ぷろみん

プログラミング的な内容を扱ってます

C++17から導入される範囲 for ループの制限緩和で何が変わるか

概要

C++17で範囲forループのbeginとendで違う型を指定できるようになりました。
範囲 for ループの制限緩和 - cpprefjp C++日本語リファレンス

これによって何が変わるのでしょうか。

イテレータの思想の違い

STLイテレータはメモリはコンテナが持ってイテレータは最小限の値のみ持つという思想で作られていました。
一方マイクロソフトの提供するAPIイテレータ自体が終端の判断をする機能を持つことが多いです。

その思想の違いをこの制限緩和は扱いやすくしてくれます。

親プロセスの名前を取得するサンプル

#include <iostream>
#include <optional>
#include <Windows.h>
#include <TlHelp32.h>

struct SelfOperationTag {};

class ProcessEntryIterator {
public:
    ProcessEntryIterator()
    {
        processEntries = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (processEntries == INVALID_HANDLE_VALUE)
        {
            result = false;
            return;
        }

        processEntry.dwSize = sizeof(PROCESSENTRY32);
        result = Process32First(processEntries, &processEntry);
    }

    ~ProcessEntryIterator()
    {
        CloseHandle(processEntries);
    }

    const PROCESSENTRY32& operator*()
    {
        return processEntry;
    }

    void operator++()
    {
        result = Process32Next(processEntries, &processEntry);
    }

    operator bool() const
    {
        return result;
    }

private:
    HANDLE processEntries;
    PROCESSENTRY32 processEntry;
    bool result;
};

bool operator!=(const ProcessEntryIterator& iterator, SelfOperationTag)
{
    return iterator;
}

class Toolhelp32Snapshot {
public:
    auto begin() const
    {
        return ProcessEntryIterator();
    }

    auto end() const
    {
        return SelfOperationTag();
    };

    static std::optional<PROCESSENTRY32> Find(DWORD processID)
    {
        for (const auto& processEntry : Toolhelp32Snapshot())
        {
            if (processEntry.th32ProcessID == processID)
            {
                return processEntry;
            }
        }
        return std::nullopt;
    }
};

int main() {
    auto currentProcessID = GetCurrentProcessId();
    auto currentProcess = Toolhelp32Snapshot::Find(currentProcessID);
    if (currentProcess)
    {
        auto parentProcess = Toolhelp32Snapshot::Find(currentProcess->th32ParentProcessID);
        if (parentProcess)
        {
            std::wcout << parentProcess->szExeFile << std::endl;
        }
    }
}

煩雑なエラーハンドリングで抜けが出てしまいにくいのではないでしょうか。下記サイトでは現実的には起こらないでしょうがProcess32Firstのエラーハンドリングが抜けています。
モジュールの列挙 (PSAPI)

正しくハンドリングしてgotoやdo whileが出てくるのも辛いものです。
Windows-classic-samples/shared.c at master · Microsoft/Windows-classic-samples · GitHub