C++17 の variant での同一型のエラーが typedef 越しだと分かりづらい2020年05月05日 12時45分32秒

C++17 から導入された variant 型。簡単にまとめると、オブジェクト指向の union 型。union は C の早い時期からある古い型だが、どの型が入っているのかは自ら処理しなければいけなかった。variant 型だと、言語側が現在の型の処理をしてくれる。また、union は型名とそれに対応する変数名で、値の参照の仕方を制御するが、variant 型には変数名が無い。

variant は型で指定するので、同じ型を複数回入れる意味は無い。

#include <variant>

int main()
{   
    std::variant< int, int > number;
    number = 3;
}
同じ型を渡すとエラーになる。
% c++ -std=c++17 variant_same_type.cpp
variant_same_type.cpp:6:12: error: no viable overloaded '='
    number = 3;
    ~~~~~~ ^ ~
/usr/include/c++/v1/variant:1215:12: note: candidate function not viable: no
      known conversion from 'int' to 'const std::__1::variant' for 1st
      argument
  variant& operator=(const variant&) = default;
           ^
/usr/include/c++/v1/variant:1216:12: note: candidate function not viable: no
      known conversion from 'int' to 'std::__1::variant' for 1st
      argument
  variant& operator=(variant&&) = default;
           ^
/usr/include/c++/v1/variant:1227:12: note: candidate template ignored:
      substitution failure [with _Arg = int, $1 = 0, _Tp = int]: no member named
      'value' in 'std::__1::__find_detail::__find_unambiguous_index_sfinae'
  variant& operator=(_Arg&& __arg) noexcept(
           ^
1 error generated.

ところがちょっと困ったことが起こった。typedef である。typedef へそれぞれの処理に都合の良い型に、名前を変換している事も多々ある。そして、int32 等のプラットフォーム非依存の、大きさ指定の型もコンパイル時に最終的な型の大きさが変わる。

#include <variant>

typedef int integer;

int main()
{   
    std::variant< int, integer > number;
    number = 3;
}
int 型と integer 型をしている。
% c++ -std=c++17 variant_typedef.cpp
variant_typedef.cpp:8:12: error: no viable overloaded '='
    number = 3;
    ~~~~~~ ^ ~
/usr/include/c++/v1/variant:1215:12: note: candidate function not viable: no
      known conversion from 'int' to 'const std::__1::variant' for 1st
      argument
  variant& operator=(const variant&) = default;
           ^
/usr/include/c++/v1/variant:1216:12: note: candidate function not viable: no
      known conversion from 'int' to 'std::__1::variant' for 1st
      argument
  variant& operator=(variant&&) = default;
           ^
/usr/include/c++/v1/variant:1227:12: note: candidate template ignored:
      substitution failure [with _Arg = int, $1 = 0, _Tp = int]: no member named
      'value' in 'std::__1::__find_detail::__find_unambiguous_index_sfinae'
  variant& operator=(_Arg&& __arg) noexcept(
           ^
1 error generated.
integer 型を int 型と報告してくれてはいるが、微妙に厄介。

次回