提问人:FizzixNerd 提问时间:7/13/2013 最后编辑:EricFizzixNerd 更新时间:12/20/2017 访问量:46188
用户定义的 C++11 枚举类默认构造函数
User Defined C++11 enum class Default Constructor
问:
有没有办法指定默认构造函数?enum class
我使用 an 来指定库中特定数据类型允许的一组值:在本例中,它是 Raspberry Pi 的 GPIO 引脚 ID 号。它看起来像这样:enum class
enum class PinID : int {N4 = 4, N17 = 17, /* ...etc... */ }
我这样做而不是仅仅使用,比如说,是为了确保代码是安全的:我可以(或者以其他方式编译时确保 - 使用的实际方法对我来说并不重要)诸如某人没有犯拼写错误(传递 5 而不是 4 等)之类的事情,并且我收到类型不匹配的自动错误消息, 等。int
static_assert
那么问题来了,它有一个默认的构造函数,为了与C的兼容,我假设(因为它们具有相同的行为) - 初始化为等价的。在这种情况下,没有价值。这意味着用户做出如下声明/定义:enum class
enum
enum class
0
0
PinID pid = PinID();
正在获取未显式定义的枚举器(甚至在查看代码时似乎不存在),并可能导致运行时错误。这也意味着,如果没有错误/默认情况,就不可能对显式定义的枚举器的值进行处理这样的技术——这是我想避免的,因为它迫使我要么做,要么做一些事情,比如返回 a ,这不太适合静态分析。switch
throw
boost::optional
我试图定义一个默认构造函数,但无济于事。我(拼命地)试图定义一个共享 名称的函数,但这(相当不出所料)导致了奇怪的编译器错误。我想保留将 to 转换为 的能力,所有枚举器都映射到各自的 ,所以仅仅“定义”,比如说,N4 = 0 是不可接受的;这是为了简单和理智。enum class
enum class
int
N#
#
我想我的问题是双重的:有没有办法获得我使用的那种静态安全?如果没有,人们还希望还有什么其他可能性?我想要的是:enum class
- 默认可构造
- 可以默认构造为任意有效值
- 提供 ES 提供的“有限指定”值集
enum class
- 至少与类型安全一样
enum class
- (最好)不涉及运行时多态性
我之所以需要默认可构造性,是因为我计划使用它来减少值与我输出到操作系统(在本例中为 sysfs)之间的转换所涉及的语法开销; 需要默认的可构造性。boost::lexical_cast
enum class
string
boost::lexical_cast
我的推理中的错误是受欢迎的——在这种情况下,我开始怀疑 es 是错误工作的正确对象;如有询问,将提供澄清。感谢您抽出宝贵时间接受采访。enum class
答:
An 只是一个强类型;它不是.C++11 只是重用了现有的关键字,以避免引入会破坏与旧版 C++ 代码兼容性的新关键字。enum class
enum
class
class
至于你的问题,没有办法确保在编译时强制转换涉及适当的候选人。考虑:
int x;
std::cin >> x;
auto p = static_cast<PinID>(x);
这是完全合法的,没有办法静态地确保控制台用户做了正确的事情。
相反,您需要在运行时检查该值是否有效。为了以自动化的方式解决这个问题,我的一位同事创建了一个生成器,用于构建这些检查以及其他有用的例程,给定一个带有枚举值的文件。您将需要找到适合您的解决方案。enum
评论
使用 或不是类定义的类型,而是作用域内枚举,并且不能定义默认构造函数。C++11 标准定义您的语句将给出零初始化。其中定义为 .它还允许枚举类型通常保存枚举器常量以外的值。enum class
enum struct
PinID pid = PinID();
PinID
enum class
要了解 PinID() 给出零初始化,需要同时阅读标准部分 3.9.9、8.5.5、8.5.7 和 8.5.10:
8.5.10 - An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized
8.5.7 - ...To value-initialize an object of type T means:
otherwise, the object is zero-initialized.
8.5.5 - To zero-initialize an object or reference of type T means: — if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;
3.9.9 - 声明枚举类型是称为标量类型的类型集的一部分。
可能的解决方案:
为了满足第 1 点到第 5 点,您可以按照以下方式编写一个类:
class PinID
{
private:
PinID(int val)
: m_value(val)
{}
int m_value;
public:
static const PinID N4;
static const PinID N17;
/* ...etc... */
PinID()
: m_value(N4.getValue())
{}
PinID(const PinID &id)
: m_value(id.getValue())
{}
PinID &operator = (const PinID &rhs)
{
m_value = rhs.getValue();
return *this;
}
int getValue() const
{
return m_value;
}
// Attempts to create from int and throw on failure.
static PinID createFromInt(int i);
friend std::istream& operator>>(std::istream &is, PinID &v)
{
int candidateVal(0);
is >> candidateVal;
v = PinID::createFromInt(candidateVal);
return is;
}
};
const PinID PinID::N4 = PinID(4);
/* ...etc... */
这可以给你一些东西,你必须做出具体的努力才能获得无效的值。默认构造函数和流运算符应允许它与lexical_cast一起使用。
似乎这取决于 PinID 创建后对 PinID 的操作有多重要,是否值得编写类或只是在使用值时处理无处不在的无效值。
评论
我知道这个问题已经过时了,并且已经有一个公认的答案,但这里有一种技术,在这种情况下可能会对 C++ 的一些新功能有所帮助
您可以声明此类的变量,也可以声明 ,它可以通过支持当前编译器所允许的多种方式来完成。non static
static
非静态:
#include <iostream>
#include <array>
template<unsigned... IDs>
class PinIDs {
private:
const std::array<unsigned, sizeof...(IDs)> ids { IDs... };
public:
PinIDs() = default;
const unsigned& operator[]( unsigned idx ) const {
if ( idx < 0 || idx > ids.size() - 1 ) {
return -1;
}
return ids[idx];
}
};
静态: - 有 3 种方法可以写这个: (第一个 - C++ 11 或 14 或更高版本) 最后 2 个 (C++17)。
不要在 C++11 部分引用我的话;我不太确定可变参数模板或参数包是什么时候首次引入的。
template<unsigned... IDs>
class PinIDs{
private:
static const std::array<unsigned, sizeof...(IDs)> ids;
public:
PinIDs() = default;
const unsigned& operator[]( unsigned idx ) const {
if ( idx < 0 || idx > ids.size() - 1 ) {
return -1;
}
return ids[idx];
}
};
template<unsigned... IDs>
const std::array<unsigned, sizeof...(IDs)> PinIDs<IDs...>::ids { IDs... };
template<unsigned... IDs>
class PinIDs{
private:
static constexpr std::array<unsigned, sizeof...(IDs)> ids { IDs... };
public:
PinIDs() = default;
const unsigned& operator[]( unsigned idx ) const {
if ( idx < 0 || idx > ids.size() - 1 ) {
return -1;
}
return ids[idx];
}
};
template<unsigned... IDs>
class PinIDs{
private:
static inline const std::array<unsigned, sizeof...(IDs)> ids { IDs... };
public:
PinIDs() = default;
const unsigned& operator[]( unsigned idx ) const {
if ( idx < 0 || idx > ids.size() - 1 ) {
return -1;
}
return ids[idx];
}
};
上面的所有示例,无论是非静态的还是静态的,都适用于下面的相同用例,并提供正确的结果:
int main() {
PinIDs<4, 17, 19> myId;
std::cout << myId[0] << " ";
std::cout << myId[1] << " ";
std::cout << myId[2] << " ";
std::cout << "\nPress any key and enter to quit." << std::endl;
char c;
std::cin >> c;
return 0;
}
输出
4 17 19
Press any key and enter to quit.
使用可变参数列表的此类类模板,您不必使用任何构造函数,而使用默认值。我确实在数组中添加了边界检查,以便不会超过其大小的边界;我可以抛出一个错误,但对于类型,我只是简单地返回 -1 作为无效值。operator[]
unsigned
对于这种类型,没有默认值,因为您必须通过模板参数列表使用单个或一组值实例化此类对象。如果需要,他们可以使用默认类型的单个参数。实例化此类对象时;它是最终的,因为它不能从其声明中更改。这是一个 const 对象,并且仍然是默认可构造的。specialize this class
0
评论