C++の無名共用体の使いみちの一例

9338 ワード

C++ の無名共用体の仕様に驚いた話という記事を読んでこんな使い方もあるよというのを思い出したので書きます。
↓のツイートでも書いたのですが文字数の関係で書きづらかったので記事にしました。


2022/04/13追記
注意
この記事で紹介している無名構造体はコンパイラ拡張の機能らしく。言語標準の機能ではありませんでした。
MSVC, gcc, clangでは使用できるはずですがこの記事の内容を実装する場合は念の為ご自身の環境で正常に動作するか確認してからでお願いします。


シェーダー言語っぽいvec4を定義する

無名共用体の中に無名構造体を入れることで同じアドレスに別名でかつネストなしでアクセスできます。
これを利用することでxyzwでもrgbaでも要素にアクセスできるvec4を定義することができます。

#include <iostream>

struct vec4
{
    union
    {
        struct
        {
            float x;
            float y;
            float z;
            float w;
        };
        struct
        {
            float r;
            float g;
            float b;
            float a;
        };
    };
};

int main()
{
    auto v = vec4();
    v.x = 1.0f;
    v.y = 2.1f;
    v.z = 3.5f;
    v.w = 4.7f;

    std::cout << v.r << ", " << v.g << ", " << v.b << ", " << v.a << std::endl;
}

実行結果

1, 2.1, 3.5, 4.7

wandboxで確認する

コードと実行結果を見てわかるようにvec4xrygzbwaがそれぞれ同じアドレスを指しているのがわかると思います。
こういうふうに実装することでシェーダー言語っぽいvec4型が実装できるのが面白いと思います。

別の書き方

ちなみに下記のようにstructunionを入れ替えたようなコードでも同じことができます。

struct vec4
{
    struct
    {
        union
        {
            float x;
            float r;
        };
        union
        {
            float y;
            float g;
        };
        union
        {
            float z;
            float b;
        };
        union
        {
            float w;
            float a;
        };
    };
};

wandboxで確認する

どの変数が同じメモリ空間にあるのかはこちらの方が見やすいかもしれませんがunionの宣言が多くなってしまいます。

配列も含む

余談ですが以下のようにunionに配列も含むと外部APIが配列のポインタを欲しがっているときに渡しやすかったりします。

struct vec4
{
    union
    {
        float elements[4];

        struct
        {
            float x;
            float y;
            float z;
            float w;
        };
        struct
        {
            float r;
            float g;
            float b;
            float a;
        };
    };
};

あとがき

無名共用体(と無名構造体)を使用するとこんなコードも書けるよという内容でした。
私はこの仕組みが結構好きで他の言語でもできればなあと思うことがあります。

おそらく大丈夫だと思いますが今回の内容が規格的に未定義動作を引き起こすようなコードであればどなたか指摘していただきたいです。
無名構造体は規格に準拠した内容ではなくコンパイラ拡張の機能だったようです。 さんありがとうございます。