In a type trait, why do people use enum rather than static const for the value?

前端 未结 5 1910
别那么骄傲
别那么骄傲 2021-02-04 02:28

For example, this is how I would write it, and it compiles and works just fine:

template struct is_pointer {
  static const bool valu         


        
5条回答
  •  刺人心
    刺人心 (楼主)
    2021-02-04 03:17

    A notable difference is in the fact that the following code compiles and links:

    template
    struct is_pointer { };
    
    template  
    struct is_pointer {
      enum { value = true };
    };     
    
    void f(const bool &b) { }
    
    int main() {
      f(is_pointer::value);
    }
    

    The following does not work instead (you get an undefined reference to value):

    template
    struct is_pointer { };
    
    template
    struct is_pointer {
      static const bool value = true;
    };
    
    void f(const bool &b) { }
    
    int main() {
      f(is_pointer::value);
    }
    

    Of course, it doesn't work unless you add somewhere the following lines:

    template
    const bool is_pointer::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
    struct is_pointer { 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.

提交回复
热议问题