简体   繁体   English

在类型特征中,为什么人们使用枚举而不是静态const作为值?

[英]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 the enum 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: 以下是一些替代方案:

  1. 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 &而不是为对象添加定义。

  2. Where the function signature cannot be changed, a temporary can be created explicitly in the call: foo( bool { Cls::mbr } ) 在无法更改函数签名的情况下,可以在调用中显式创建临时函数: foo( bool { Cls::mbr } )

  3. However, this is compile time information! 但是,这是编译时的信息! Therefore foo should be a template with a T and T* overload, or be specialized with bool . 因此, foo应该是具有TT*重载的模板,或者是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.

相关问题 为什么 main() 参数 argv 的类型是 char*[] 而不是 const char*[]? - Why is main() argument argv of type char*[] rather than const char*[]? 枚举成员值的类型特征 - Type trait for enum member value 为什么它是const指针而不是const指针? - Why it is a const pointer rather than pointer to const? 模板特征类中的枚举和静态const成员变量用法 - enum and static const member variable usage in template trait class 为什么“gptr”类型为basic_streambuf char_type *而不是const char_type *? - Why is the “gptr” type of basic_streambuf char_type* rather than const char_type*? 为什么返回const Rational而不是Rational - Why return const Rational rather than Rational “for(;;)”比“while (TRUE)”快吗? 如果不是,人们为什么要使用它? - Is “for(;;)” faster than “while (TRUE)”? If not, why do people use it? 为什么`std :: pair`将`std :: tuple`作为ctor参数类型而不是`const std :: tuple&`? - Why does `std::pair` take `std::tuple` as ctor argument type rather than `const std::tuple&`? 为什么人们在 C++ 中使用枚举作为常量而他们可以使用 const? - Why do people use enums in C++ as constants while they can use const? static const成员值与成员枚举:哪种方法更好,为什么? - static const Member Value vs. Member enum : Which Method is Better & Why?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM