[英]In a type trait, why do people use enum rather than static const for the value?
For example, this is how I would write it, and it compiles and works just fine: 例如,这就是我写它的方式,它编译并且工作得很好:
template<typename T> struct is_pointer<T*> {
static const bool value = true;
}
Then why do some people write the less obvious 那么为什么有些人写的不太明显
template<typename T> struct is_pointer<T*> {
enum { value = true };
}
instead? 代替? Is it only because the
static const
variable uses a byte of memory, whereas the enum
doesn't? 是不是因为
static const
变量使用了一个字节的内存,而enum
不是?
A notable difference is in the fact that the following code compiles and links: 一个值得注意的区别在于以下代码编译和链接:
template<typename>
struct is_pointer { };
template<typename T>
struct is_pointer<T*> {
enum { value = true };
};
void f(const bool &b) { }
int main() {
f(is_pointer<void*>::value);
}
The following does not work instead (you get an undefined reference to value
): 以下代码不起作用(您获得对
value
的未定义引用 ):
template<typename>
struct is_pointer { };
template<typename T>
struct is_pointer<T*> {
static const bool value = true;
};
void f(const bool &b) { }
int main() {
f(is_pointer<void*>::value);
}
Of course, it doesn't work unless you add somewhere the following lines: 当然,除非你添加以下几行,否则它不起作用:
template<typename T>
const bool is_pointer<T*>::value;
That is because of [class.static.data]/3 (emphasis mine): 那是因为[class.static.data] / 3 (强调我的):
If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression ([expr.const]).
如果非易失性非内联const静态数据成员是整数或枚举类型,则它在类定义中的声明可以指定一个大括号或大小为初始化器,其中作为赋值表达式的每个initializer子句都是一个常量表达式([expr.const])。 The member shall still be defined in a namespace scope if it is odr-used ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer.
如果程序中的odr-used ([basic.def.odr])并且命名空间作用域定义不包含初始化程序,则仍应在名称空间作用域中定义该成员。 [...]
[...]
In other terms, static const bool value = true;
换句话说,
static const bool value = true;
is a declaration, not a definition and you cannot odr-use value
. 是一个声明,而不是一个定义,你不能
value
。
On the other side, according with [dcl.enum/1] (emphasis mine): 另一方面,根据[dcl.enum / 1] (强调我的):
An enumeration is a distinct type with named constants .
枚举是具有命名常量的不同类型。
Those named constants can be const referenced as shown in the example above. 那些命名常量可以被const引用,如上例所示。
As a side note, something similar applies if you use static
constexpr
data members in C++11/14: 作为旁注,如果在C ++
static
使用static
constexpr
数据成员,则类似的情况也适用:
template<typename T>
struct is_pointer<T*> { static constexpr bool value = true; };
This doesn't work as well and that's how I discovered the subtle differences between them. 这不起作用,这就是我发现它们之间的微妙差异的方式。
I found help here on SO getting some nice hints out of the answer I've been given. 我在这里找到了帮助,因为我得到了一些很好的提示。
References to the standard are a plus to better explain what's going on under the hood. 参考标准是一个加号,以更好地解释在幕后发生的事情。
Note that a static
constexpr
data member declaration like the one above is also a definition since C++17. 请注意,像上面那样的
static
constexpr
数据成员声明也是自C ++ 17以来的定义。 Therefore you won't have to define it anymore and you'll be able to odr-use it directly instead. 因此,您不必再定义它,您将能够直接使用它来代替它。
As mentioned in the comments (thanks to @Yakk that confirmed this) I'm also trying to explain how it happens that the above mentioned named constants bind to a const reference. 正如评论中提到的那样(感谢@Yakk确认了这一点)我也试图解释上面提到的命名常量如何绑定到const引用。
[expr.const/3] introduces the integral constant expression and mentions unscoped enum
s by saying that it's implicitly converted to a prvalue . [expr.const / 3]引入了整数常量表达式,并通过说它隐式转换为prvalue来提及未编译的
enum
。
[dcl.init.ref/5] and [class.temporary/2] do the rest, for they rule on reference binding and temporaries. [dcl.init.ref / 5]和[class.temporary / 2]完成剩下的工作,因为他们统治着引用绑定和临时。
Is it only because the
static const
variable uses a byte of memory, whereas theenum
doesn't?是不是因为
static const
变量使用了一个字节的内存,而enum
不是?
Yes, that's the reason. 是的,这就是原因。
static const bool value = true;
would occupy memory, while 会占据记忆,而
enum { value = true };
doesn't. 没有。
Yes you are correct: enum { value = true };
是的你是对的:
enum { value = true };
doesn't occupy any memory. 不占用任何记忆。
Furthermore, prior to C++11 it was pretty much the only way of achieving this: static const bool value = true;
此外,在C ++ 11之前,它几乎是实现此目的的唯一方法:
static const bool value = true;
is only legal in a class definition from C++11 onwards. 从C ++ 11开始,它在类定义中是合法的。 Although a
constexpr
might be preferred. 虽然
constexpr
可能是首选。
It also is another symbol in every object file that includes it, to no benefit. 它也是包含它的每个目标文件中的另一个符号,没有任何好处。 If you use symbol folding (--gc-sections) you'll run out of separatable sections & bloat your binary.
如果你使用符号折叠(--gc-sections),你将耗尽可分离的部分并膨胀你的二进制文件。
Some people write the less obvious enum
rather than static bool const
because they don't realize that there are other changes they should make. 有些人写的是不太明显的
enum
而不是static bool const
因为他们没有意识到他们应该做出其他改变。
C++ requires the object to be defined if it's address is needed, for example if it's passed to this function foo
: 如果需要地址,C ++需要定义对象,例如,如果它被传递给这个函数
foo
:
void foo(bool const &);
However, solving the issue by defining the object is actually not the correct fix for this problem. 但是,通过定义对象来解决问题实际上并不是解决此问题的正确方法。 Here are some alternatives:
以下是一些替代方案:
Small objects should not be passed by reference . 不应通过引用传递小对象 。 The change should be to remove
const &
from the function signature, not add a definition for the object. 更改应该是从函数签名中删除
const &
, 而不是为对象添加定义。
Where the function signature cannot be changed, a temporary can be created explicitly in the call: foo( bool { Cls::mbr } )
在无法更改函数签名的情况下,可以在调用中显式创建临时函数:
foo( bool { Cls::mbr } )
However, this is compile time information! 但是,这是编译时的信息! Therefore
foo
should be a template with a T
and T*
overload, or be specialized with bool
. 因此,
foo
应该是具有T
和T*
重载的模板,或者是bool
专用的模板。
This 3rd solution has the benefit of removing an unnecessary run time check (hopefully optimized by the compiler) and also allowing for the pointer and non-pointer case to be handled independently, possibly making the code clearer. 第三种解决方案的好处是可以删除不必要的运行时检查(希望由编译器优化),并且还允许独立处理指针和非指针的情况,可能使代码更清晰。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.