如何迭代枚举 class 的枚举器?
How to iterate over enumerators of an enum class?
有没有办法用 enum class
的 enumerators 来初始化容器(例如 std::unordered_set<char>
)?
我有这个class:
#include <iostream>
#include <unordered_set>
class Foo
{
public:
inline static const std::unordered_set<char> chars_for_drawing { '/', '\', '|', '-' };
};
int main( )
{
for ( const char ch : Foo::chars_for_drawing )
{
std::cout << ch << ' ';
}
}
但我希望 chars_for_drawing
集用枚举数初始化:
#include <iostream>
#include <unordered_set>
class Foo
{
public:
enum class AllowedChars : char
{
ForwardSlash = '/',
BackSlash = '\',
VerticalSlash = '|',
Dash = '-'
};
// inline static const std::unordered_set<char> chars_for_drawing { '/', '\', '|', '-' }; // not like this
inline static const std::unordered_set<char> chars_for_drawing {
static_cast<char>( AllowedChars::ForwardSlash ),
static_cast<char>( AllowedChars::BackSlash ),
static_cast<char>( AllowedChars::VerticalSlash ),
static_cast<char>( AllowedChars::Dash )
};
};
int main( )
{
for ( const char ch : Foo::chars_for_drawing )
{
std::cout << ch << ' ';
}
}
可以看出,第二种做法有点乱。有没有办法遍历枚举数并将它们分配给 unordered_set
?也许通过使用 lambda?
没有直接的方法。人们经常忘记的一件事:枚举值的范围由其基础类型决定。枚举器只是一些命名常量。您的枚举:
enum class AllowedChars : char
{
ForwardSlash = '/',
BackSlash = '\',
VerticalSlash = '|',
Dash = '-'
};
有助于迭代 a
struct {
char value;
static const char ForwardSlash = '/';
static const char BackSlash = '\';
static const char VerticalSlash = '|';
static const char Dash = '-';
};
会:完全不会。
当枚举数具有连续值时,情况就不同了,有时使用的 hack 是使用特殊的枚举数来表示“大小”:
enum class AllowedChars : char
{
ForwardSlash,
BackSlash,
VerticalSlash,
Dash,
SIZE
};
int main() {
for (int i=0;i< static_cast<int>(AllowedChars::SIZE); ++i){
std::cout << i;
}
}
仅此一点有点愚蠢,因为到实际字符的映射丢失了。但是,它可以由数组提供:
#include <iostream>
enum class AllowedCharNames : char
{
ForwardSlash,
BackSlash,
VerticalSlash,
Dash,
SIZE
};
char AllowedChars[] = {'/','\','|','-'};
int main() {
std::cout << AllowedChars[static_cast<size_t>(AllowedCharNames::Dash)];
for (int i=0;i< static_cast<int>(AllowedCharNames::SIZE); ++i){
std::cout << AllowedChars[i];
}
}
TL;DR 重新考虑枚举是否适合这项工作。枚举的实际作用常常被高估。有时不是枚举是更好的选择。
在 C++ 中,enum
和 enum class
不提供您想要的那种功能。
您可以自己实现该功能。有相当多的样板文件,但它可能是值得的,因为它会使调用站点更容易使用。
根据 enum
或 enum class
的枚举定义,实现例程可以容纳有间隙的序列。
这是一个简单的例子 enum class
,其中有间隙。
#include <iostream>
#include <stdexcept>
#include <utility>
using std::cout;
using std::logic_error;
using std::ostream;
using std::underlying_type_t;
namespace {
enum class Lorem {
ipsum = 10, dolor = 20, sit = 30, amet = 100, consectetur = 105, adipiscing = 111
};
auto Lorem_first() -> Lorem { return Lorem::ipsum; }
auto Lorem_last() -> Lorem { return Lorem::adipiscing; }
auto operator<<(ostream& out, Lorem e) -> ostream& {
switch(e) {
#define CASE(x) case Lorem::x: return out << #x
CASE(ipsum);
CASE(dolor);
CASE(sit);
CASE(amet);
CASE(consectetur);
CASE(adipiscing);
#undef CASE
}
throw logic_error("operator<< unknown Lorem");
}
auto operator+(Lorem e) -> underlying_type_t<decltype(e)> {
return static_cast<underlying_type_t<decltype(e)>>(e);
}
auto operator++(Lorem& e) -> Lorem& {
if (e == Lorem_last()) throw logic_error("operator++(Lorem&)");
switch(e) {
#define CASE(x, y) case Lorem::x: e = Lorem::y; break
CASE(ipsum, dolor);
CASE(dolor, sit);
CASE(sit, amet);
CASE(amet, consectetur);
CASE(consectetur, adipiscing);
#undef CASE
case Lorem::adipiscing: break;
}
return e;
}
class Lorem_Range {
bool done = false;
Lorem iter = Lorem_first();
public:
auto begin() const -> Lorem_Range const& { return *this; }
auto end() const -> Lorem_Range const& { return *this; }
auto operator*() const -> Lorem { return iter; }
bool operator!=(Lorem_Range const&) const { return !done; }
void operator++() {
if (done) {
throw logic_error("Lorem_Range::operator++");
}
if (iter == Lorem_last()) {
done = true;
} else {
++iter;
}
}
};
} // anon
int main() {
for (auto e : Lorem_Range()) {
cout << +e << ", " << e << "\n";
}
}
有没有办法用 enum class
的 enumerators 来初始化容器(例如 std::unordered_set<char>
)?
我有这个class:
#include <iostream>
#include <unordered_set>
class Foo
{
public:
inline static const std::unordered_set<char> chars_for_drawing { '/', '\', '|', '-' };
};
int main( )
{
for ( const char ch : Foo::chars_for_drawing )
{
std::cout << ch << ' ';
}
}
但我希望 chars_for_drawing
集用枚举数初始化:
#include <iostream>
#include <unordered_set>
class Foo
{
public:
enum class AllowedChars : char
{
ForwardSlash = '/',
BackSlash = '\',
VerticalSlash = '|',
Dash = '-'
};
// inline static const std::unordered_set<char> chars_for_drawing { '/', '\', '|', '-' }; // not like this
inline static const std::unordered_set<char> chars_for_drawing {
static_cast<char>( AllowedChars::ForwardSlash ),
static_cast<char>( AllowedChars::BackSlash ),
static_cast<char>( AllowedChars::VerticalSlash ),
static_cast<char>( AllowedChars::Dash )
};
};
int main( )
{
for ( const char ch : Foo::chars_for_drawing )
{
std::cout << ch << ' ';
}
}
可以看出,第二种做法有点乱。有没有办法遍历枚举数并将它们分配给 unordered_set
?也许通过使用 lambda?
没有直接的方法。人们经常忘记的一件事:枚举值的范围由其基础类型决定。枚举器只是一些命名常量。您的枚举:
enum class AllowedChars : char
{
ForwardSlash = '/',
BackSlash = '\',
VerticalSlash = '|',
Dash = '-'
};
有助于迭代 a
struct {
char value;
static const char ForwardSlash = '/';
static const char BackSlash = '\';
static const char VerticalSlash = '|';
static const char Dash = '-';
};
会:完全不会。
当枚举数具有连续值时,情况就不同了,有时使用的 hack 是使用特殊的枚举数来表示“大小”:
enum class AllowedChars : char
{
ForwardSlash,
BackSlash,
VerticalSlash,
Dash,
SIZE
};
int main() {
for (int i=0;i< static_cast<int>(AllowedChars::SIZE); ++i){
std::cout << i;
}
}
仅此一点有点愚蠢,因为到实际字符的映射丢失了。但是,它可以由数组提供:
#include <iostream>
enum class AllowedCharNames : char
{
ForwardSlash,
BackSlash,
VerticalSlash,
Dash,
SIZE
};
char AllowedChars[] = {'/','\','|','-'};
int main() {
std::cout << AllowedChars[static_cast<size_t>(AllowedCharNames::Dash)];
for (int i=0;i< static_cast<int>(AllowedCharNames::SIZE); ++i){
std::cout << AllowedChars[i];
}
}
TL;DR 重新考虑枚举是否适合这项工作。枚举的实际作用常常被高估。有时不是枚举是更好的选择。
在 C++ 中,enum
和 enum class
不提供您想要的那种功能。
您可以自己实现该功能。有相当多的样板文件,但它可能是值得的,因为它会使调用站点更容易使用。
根据 enum
或 enum class
的枚举定义,实现例程可以容纳有间隙的序列。
这是一个简单的例子 enum class
,其中有间隙。
#include <iostream>
#include <stdexcept>
#include <utility>
using std::cout;
using std::logic_error;
using std::ostream;
using std::underlying_type_t;
namespace {
enum class Lorem {
ipsum = 10, dolor = 20, sit = 30, amet = 100, consectetur = 105, adipiscing = 111
};
auto Lorem_first() -> Lorem { return Lorem::ipsum; }
auto Lorem_last() -> Lorem { return Lorem::adipiscing; }
auto operator<<(ostream& out, Lorem e) -> ostream& {
switch(e) {
#define CASE(x) case Lorem::x: return out << #x
CASE(ipsum);
CASE(dolor);
CASE(sit);
CASE(amet);
CASE(consectetur);
CASE(adipiscing);
#undef CASE
}
throw logic_error("operator<< unknown Lorem");
}
auto operator+(Lorem e) -> underlying_type_t<decltype(e)> {
return static_cast<underlying_type_t<decltype(e)>>(e);
}
auto operator++(Lorem& e) -> Lorem& {
if (e == Lorem_last()) throw logic_error("operator++(Lorem&)");
switch(e) {
#define CASE(x, y) case Lorem::x: e = Lorem::y; break
CASE(ipsum, dolor);
CASE(dolor, sit);
CASE(sit, amet);
CASE(amet, consectetur);
CASE(consectetur, adipiscing);
#undef CASE
case Lorem::adipiscing: break;
}
return e;
}
class Lorem_Range {
bool done = false;
Lorem iter = Lorem_first();
public:
auto begin() const -> Lorem_Range const& { return *this; }
auto end() const -> Lorem_Range const& { return *this; }
auto operator*() const -> Lorem { return iter; }
bool operator!=(Lorem_Range const&) const { return !done; }
void operator++() {
if (done) {
throw logic_error("Lorem_Range::operator++");
}
if (iter == Lorem_last()) {
done = true;
} else {
++iter;
}
}
};
} // anon
int main() {
for (auto e : Lorem_Range()) {
cout << +e << ", " << e << "\n";
}
}