表題の通り。
一時しのぎの例
もっと良い方法がある人は教えて下さい![]()
![]()
# include <iostream>
# include <cassert>
/* 旧バージョンの列挙体と列挙子
enum class HogeEnum
{
Taro,
Jiro,
Sabro
};
*/
/* 新バージョンの列挙体と列挙子。旧バージョン相当のコードを書き換えることなく、この新定義を使いたい */
enum class NewHogeEnum
{
NewTaro,
NewJiro,
NewSabro
};
/* HogeEnum を NewHogeEnum 型のエイリアスとすることはできるが、列挙子の表記は旧バージョンを引き継げないためコンパイルエラー
using HogeEnum = NewHogeEnum;
*/
/* 列挙子の表記は旧バージョンを引き継げるが、HogeEnum を NewHogeEnum 型のエイリアスとすることはできないためコンパイルエラー
namespace HogeEnum {
constexpr NewHogeEnum Taro = NewHogeEnum::NewTaro;
constexpr NewHogeEnum Jiro = NewHogeEnum::NewJiro;
constexpr NewHogeEnum Sabro = NewHogeEnum::NewSabro;
}
*/
/* "HogeEnum::Taro" のようなコードにおいて、 Taro は NewHogeEnum の列挙子ではないためコンパイルエラー。
using HogeEnum = NewHogeEnum;
constexpr NewHogeEnum Taro = NewHogeEnum::NewTaro;
constexpr NewHogeEnum Jiro = NewHogeEnum::NewJiro;
constexpr NewHogeEnum Sabro = NewHogeEnum::NewSabro;
*/
/* コンパイル成功。ただし必要に応じて演算子オーバロード等の定義を追加する必要がある。そもそも型として全く別物を定義することになる
struct HogeEnum
{
static constexpr NewHogeEnum Taro = NewHogeEnum::NewTaro;
static constexpr NewHogeEnum Jiro = NewHogeEnum::NewJiro;
static constexpr NewHogeEnum Sabro = NewHogeEnum::NewSabro;
HogeEnum(const NewHogeEnum& NewHogeEnumVar) : value(NewHogeEnumVar) {};
HogeEnum& operator=(const NewHogeEnum& NewHogeEnumVar)
{
value = NewHogeEnumVar;
return *this;
}
operator int() const { return static_cast<int>(value); }
bool operator ==(const NewHogeEnum& NewHogeEnumVar) { return (value == NewHogeEnumVar); }
bool operator !=(const NewHogeEnum& NewHogeEnumVar) { return (value != NewHogeEnumVar); }
private:
NewHogeEnum value = NewHogeEnum::NewTaro;
};
constexpr NewHogeEnum HogeEnum::Taro;
constexpr NewHogeEnum HogeEnum::Jiro;
constexpr NewHogeEnum HogeEnum::Sabro;
*/
/* コンパイル成功。ただしコード中のすべての Taro、Jiro、Sabro という文字列が置換されてしまう */
using HogeEnum = NewHogeEnum;
# define Taro NewTaro
# define Jiro NewJiro
# define Sabro NewSabro
int main()
{
static_assert(static_cast<int>(HogeEnum::Taro) == 0, "");
static_assert(static_cast<int>(HogeEnum::Jiro) == 1, "");
static_assert(static_cast<int>(HogeEnum::Sabro) == 2, "");
HogeEnum hoge = HogeEnum::Taro;
assert(hoge == HogeEnum::Taro);
std::cout << static_cast<int>(hoge) << std::endl;
hoge = HogeEnum::Jiro;
assert(hoge == HogeEnum::Jiro);
std::cout << static_cast<int>(hoge) << std::endl;
hoge = HogeEnum::Sabro;
assert(hoge == HogeEnum::Sabro);
std::cout << static_cast<int>(hoge) << std::endl;
HogeEnum fuga(hoge);
assert(fuga == HogeEnum::Sabro);
hoge = fuga;
assert(hoge == HogeEnum::Sabro);
std::cout << static_cast<int>(hoge) << std::endl;
return 0;
}
using による列挙型の、マクロによる列挙子のエイリアスをそれぞれ定義するのが (思いついた限りでは) 一番良い方法に見えたがどうなんだろうか。
マクロによる一時しのぎが通用しないケース
上記コードにこんなコメントを書いた
コード中のすべての Taro、Jiro、Sabro という文字列が置換されてしまう
実際、下記のように改造したコードではマクロによる一時しのぎは通用せずコンパイルエラーを引き起こす。
# include <iostream>
# include <cassert>
/* 旧バージョンの列挙体と列挙子
enum class HogeEnum
{
Taro,
Jiro,
Sabro
};
*/
/* 新バージョンの列挙体と列挙子。旧バージョン相当のコードを書き換えることなく、この新定義を使いたい */
enum class NewHogeEnum
{
NewTaro,
NewJiro,
NewSabro
};
/* 新規に追加した列挙体定義。これに起因するコンパイルエラーが起きる */
enum class FugaEnum
{
Taro,
Jiro,
Sabro
};
/* FugaEnum の列挙子 Taro、Jiro、Sabro も置換されてしまうのでコンパイルエラー */
using HogeEnum = NewHogeEnum;
# define Taro NewTaro
# define Jiro NewJiro
# define Sabro NewSabro
int main()
{
static_assert(static_cast<int>(HogeEnum::Taro) == 0, "");
static_assert(static_cast<int>(HogeEnum::Jiro) == 1, "");
static_assert(static_cast<int>(HogeEnum::Sabro) == 2, "");
static_assert(static_cast<int>(FugaEnum::Taro) == 0, ""); // <-- ここでエラー
...
}
上記コードでもコンパイルに成功する方法を @SaitoAtsushi さんが教えてくれました!ありがとうございます!(詳細は本記事のコメント欄を参照)