那么为什么有些人写不太明显的
模板< typename T> struct is_pointer< T *> { enum {value = true}; }代替?这只是因为 static const 变量使用一个字节的内存,而枚举不是? >
解决方案一个显着的区别在于以下代码编译和链接:
模板<类型名> struct is_pointer {}; 模板< typename T> struct is_pointer< T *> { enum {value = true}; }; void f(const bool& b){} int main(){ f(is_pointer< void *> :: value); }以下内容不起作用(您获得了一个未定义的引用到值):
template< typename> struct is_pointer {}; 模板< typename T> struct is_pointer< T *> { static const bool value = true; }; void f(const bool& b){} int main(){ f(is_pointer< void *> :: value); }当然,它不起作用,除非你添加以下行:
模板< typename T> const bool is_pointer< T *> :: value;这是因为 [class.static.data] / 3 (强调我的):
如果非易失性非内联const静态数据成员是整数或枚举类型,则它在类定义中的声明可以指定一个大括号或等于初始化器,其中每个initializer子句为赋值表达式是一个常量表达式([expr.const])。 如果在程序中使用odr使用([basic.def.odr]),则该名称仍将在命名空间范围内定义,并且命名空间范围定义不得包含初始化程序。 [...]
其他术语 static const bool value = true; 是一个声明,而不是一个定义,你不能odr使用值。 另一方面,根据 [dcl.enum / 1] (强调我的):
枚举是具有命名常量的不同类型。
这些命名的常量可以被引用,如上面的例子所示。
作为附注,如果您使用 static constexpr C ++ 11/14中的数据成员:
模板< ; typename T> struct is_pointer< T *> {static constexpr bool value = true; };这不起作用,这就是我发现它们之间微妙的区别。 >
我在这里找到帮助,因此我得到了一些很好的提示。 参考标准是一个加号,以更好地解释什么是请注意, static constexpr 数据成员声明如上所述也是自C ++ 17以来的定义。因此,您不必再定义它,您可以直接使用它。
如评论中所提到的(感谢@Yakk证实了这一点),我也试图解释如何发生,上面提到的命名常量绑定到一个const引用。
[expr.const / 3] 引入整数常量表达式,并提及未限定的枚举,表示将其隐式转换为 prvalue 。 [dcl.init.ref / 5] 和 [class.temporary / 2] 做其他事情,因为他们统治参考约束和临时性。
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?
解决方案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):
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):
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]). 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. [...]
In other terms, static const bool value = true; is a declaration, not a definition and you cannot odr-use value. On the other side, according with [dcl.enum/1] (emphasis mine):
An enumeration is a distinct type with named constants.
Those named constants can be const referenced as shown in the example above.
As a side note, something similar applies if you use static constexpr data members in C++11/14:
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. 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.
[expr.const/3] introduces the integral constant expression and mentions unscoped enums by saying that it's implicitly converted to a prvalue. [dcl.init.ref/5] and [class.temporary/2] do the rest, for they rule on reference binding and temporaries.
更多推荐
在类型特征中,为什么人们使用枚举而不是静态const来获取价值?
发布评论