---
# System prepended metadata

title: Strong Alias
tags: [Chromium-General, elkurin-daily-note]

---

# Strong Alias

[base::StrongAlias](https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h;l=78;drc=b337f048544d14eeb1e8aa6ae1b54ec796936ba5)は型安全なtypedef。

例えば実体がどちらもint型の２つの型を`using`で定義した場合、以下のように互いの区別がつかない。
```cpp=
using Orange = int;
using Apple = int;

Apple apple(2);
// Apple -> Orange への型変換をしちゃう
Orange orange = apple;
// Orange と Apple の operationをしちゃう
Orange x = orange + apple;
if (orange > apple);

// コンパイルエラー：OrangeとAppleそれぞれでオーバーロードしたいのにできない
void foo(Orange orange);
void foo(Apple apple);
```

Strong Aliasはこれをいい感じにしてくれる。

## 概要
StrongAliasは以下のように定義する。
```cpp=
using Orange = StrongAlias<class OrangeTag, int>;
```

ひとつめの型引数は`TagType`でこれは空のクラス。タグ用に使うだけ。  
ふたつめの型引数は`UnderlyingType`で実体の値の型をあらわす。

中身の値は`orange.value()`または`*orange`といった感じで取り出すことができる。std::optionalと同じ。

StrongAliasを使うと上記の例がどうなるかと言うと
```cpp=
using Orange = StrongAlias<class OrangeTag, int>;
using Apple = StrongAliasM<class AppleTag, int>;

Apple apple(2);
// コンパイルエラー：中身は同じでも型が不一致
Orange orange = apple;
// コンパイルエラー：違う型同士なので演算不可
Orange x = orange + apple;
if (orange > apple);

// 別々のオーバーロード関数としてコンパイル可
void foo(Orange orange);
void foo(Apple apple);
```

これは例えば[IDの実装](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/attribution_reporting/attribution_report.h;l=43;drc=c686e8f4fd379312469fe018f5c390e9c8f20d0d)に使われる。
```cpp=
using Id = base::StrongAlias<AttributionReport, int64_t>;
```
中身はただのintだが、例えばIDがいくつかあってそれらを別々に扱いたい時に多分保全しやすい。

## 実装

中身は比較的シンプル。  
UnderlyingTypeである[`value_`](https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h;l=144;drc=b337f048544d14eeb1e8aa6ae1b54ec796936ba5)を持ち、ここに[constructor](https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h;l=83-85;drc=b337f048544d14eeb1e8aa6ae1b54ec796936ba5)で値をセットする。  
これは`operator*()`や`value()`で取り出すことができるようになっている。

比較演算はUnderlyingTypeのdefaultを使用する。
```cpp=
friend auto operator<=>(const StrongAlias& lhs,
                        const StrongAlias& rhs) = default;
friend bool operator==(const StrongAlias& lhs,
                       const StrongAlias& rhs) = default;
```
比較演算を使いたいならUnderlyingTypeの`operator<`などを定義しておく必要がある。

### Hasher
unordered_mapやunordered_setの要素としてStrongAliasを使おうとすると問題が発生する。
```cpp=
using MyType = base::StrongAlias<...>;
using MySet = std::unordered_set<MyType>;
```
曰くstd::hashの特殊化はcoding規約的に駄目らしい。

hashを使うために[StrongAlias::Hasher](https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h;l=127;drc=b337f048544d14eeb1e8aa6ae1b54ec796936ba5)が定義されている。

```cpp=
struct Hasher {
  using argument_type = StrongAlias;
  using result_type = std::size_t;
  result_type operator()(const argument_type& id) const {
    return std::hash<UnderlyingType>()(id.value());
  }
};
```

これは以下のように使う。
```cpp=
using MyType = base::StrongAlias<...>;
using MySet = std::unordered_set<MyType, typename MyType::Hasher>;
```
