C++　メタ関数 is_convertible を再実装してみた

LLVMの実装を参考に、C++11で追加されたis_convertibleの簡易版を実装しました。
興味深い実装だったので、メモも兼ねてまとめます。

## is_convertibleとは

型Fromから型Toに、暗黙的に変換可能か調べるメタ関数[^メタ関数]です。
ライブラリの実装では、参照も区別するのですが、今回は参照を区別しない簡易版の実装とします。

[^メタ関数]: meta function, <type_traits>ヘッダで定義される型の特性を判定、操作するためクラステンプレート。[type_traits - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/reference/type_traits.html)

## 実装

やっていることは架空の関数`test`を定義して、暗黙の変換を用いてどちらの関数が使用されるのか、確認するというものです。

```cpp
typedef char yes_type;
struct no_type {
  char dummy[8];
};

template <class From, class To>
struct is_convertible {
  static From       val;
  static no_type    test(...);
  static yes_type   test(To);
  static bool const value = sizeof(test(val)) == sizeof(yes_type);
};
```

実装を見てみるとなるほど、という感じでした。テンプレートの奥深さを知れる実装です。

## テスト

google testを使用して、テストも書いてみました。
実装に対して、テストが長いので、折りたたみました。

<details><summary>ここをクリック！</summary><div>

```cpp
#include <gtest/gtest.h>

#include <type_traits>
#include <typeinfo>

#include "type_traits.hpp"

class IsConvertibleTest : public ::testing::Test {};

template <class From, class To>
void test_is_convertible() {
  bool ft  = ft::is_convertible<From, To>::value;
  bool std = std::is_convertible<From, To>::value;
  EXPECT_EQ(ft, std);
}

TEST_F(IsConvertibleTest, IsConvertibleTest_int) {
  test_is_convertible<int, int>();
  test_is_convertible<int, unsigned int>();
  test_is_convertible<int, unsigned long>();
  test_is_convertible<int, unsigned long long>();
  test_is_convertible<int, long>();
  test_is_convertible<int, long long>();
  test_is_convertible<int, char>();
  test_is_convertible<int, signed char>();
  test_is_convertible<int, unsigned char>();
  test_is_convertible<int, short>();
  test_is_convertible<int, unsigned short>();
  test_is_convertible<int, float>();
  test_is_convertible<int, double>();
  test_is_convertible<int, long double>();
  test_is_convertible<int, void*>();
  test_is_convertible<int, int*>();
  test_is_convertible<int, int&>();
}

TEST_F(IsConvertibleTest, IsConvertibleTest_qualifiers) {
    test_is_convertible<const int, int>();
    test_is_convertible<const int, unsigned int>();
    test_is_convertible<const int, unsigned long>();
    test_is_convertible<const int, unsigned long long>();
    test_is_convertible<const int, long>();
    test_is_convertible<const int, long long>();
    test_is_convertible<const int, char>();
    test_is_convertible<const int, signed char>();
    test_is_convertible<const int, unsigned char>();
    test_is_convertible<const int, short>();
    test_is_convertible<const int, unsigned short>();
    test_is_convertible<const int, float>();
    test_is_convertible<const int, double>();
    test_is_convertible<const int, long double>();
    test_is_convertible<const int, void*>();
    test_is_convertible<const int, int*>();
    test_is_convertible<const int, int&>();

    test_is_convertible<volatile int, int>();
    test_is_convertible<volatile int, unsigned int>();
    test_is_convertible<volatile int, unsigned long>();
    test_is_convertible<volatile int, unsigned long long>();
    test_is_convertible<volatile int, long>();
    test_is_convertible<volatile int, long long>();
    test_is_convertible<volatile int, char>();
    test_is_convertible<volatile int, signed char>();
    test_is_convertible<volatile int, unsigned char>();
    test_is_convertible<volatile int, short>();
    test_is_convertible<volatile int, unsigned short>();
    test_is_convertible<volatile int, float>();
    test_is_convertible<volatile int, double>();
    test_is_convertible<volatile int, long double>();
    test_is_convertible<volatile int, void*>();
    test_is_convertible<volatile int, int*>();
    test_is_convertible<volatile int, int&>();
}
```

</div></details>



## 参考

[std::is_convertible, std::is_nothrow_convertible - cppreference.com](https://ja.cppreference.com/w/cpp/types/is_convertible)

https://github.com/llvm/llvm-project