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

时间:2016-10-13 10:37:34

标签: c++ templates enums static const

例如,这是我写它的方式,它编译并且工作得很好:

template<typename T> struct is_pointer<T*> {
  static const bool value = true;
}

那么为什么有些人会写不太明显的

template<typename T> struct is_pointer<T*> {
  enum { value = true };
}      

代替?是不是因为static const变量使用了一个字节的内存,而enum没有?

5 个答案:

答案 0 :(得分:23)

显着的区别在于以下代码编译和链接:

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);
}

以下内容不起作用(您获得未定义的引用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);
}

当然,除非你添加以下几行,否则它不起作用:

template<typename T>
const bool is_pointer<T*>::value;

那是因为[class.static.data]/3(强调我的):

  

如果非易失性非内联const静态数据成员是整数类型或枚举类型,则其在类定义中的声明可以指定一个大括号或者相等初始化程序,其中每个initializer子句都是赋值表达式是一个常量表达式([expr.const])。 如果程序中使用了odr-used ([basic.def.odr]),则成员仍应在命名空间作用域中定义,并且命名空间作用域定义不应包含初始化程序。 [...]

换句话说,static const bool value = true;是一个声明,而不是一个定义,你不能使用value
另一方面,根据[dcl.enum/1](强调我的):

  

枚举是具有命名常量的不同类型

这些命名常量可以被const引用,如上例所示。

作为旁注,如果您在C ++ 11/14中使用static constexpr数据成员,则会出现类似情况:

template<typename T>
struct is_pointer<T*> { static constexpr bool value = true; }; 

这不起作用,这就是我发现它们之间的微妙差异的方式。

我在这里找到了帮助,因为我得到了一些很好的提示 参考标准是一个优点,可以更好地解释幕后发生的事情。

请注意,像上面那样的static constexpr数据成员声明也是自C ++ 17以来的定义。因此,您不必再定义它了,您将能够直接使用它。

正如评论中提到的那样(感谢@Yakk确认了这一点)我也试图解释上面提到的命名常量如何绑定到const引用。

[expr.const/3]引入了整型常量表达式,并提到未编译的enum,并将其隐式转换为 prvalue [dcl.init.ref/5][class.temporary/2]完成剩下的工作,因为他们会依赖参考绑定和临时工具。

答案 1 :(得分:6)

  

是否只是因为static const变量使用了一个字节的内存,而enum没有?

是的,这就是原因。

static const bool value = true;

会占用记忆,而

enum { value = true };

没有按&#39;吨

答案 2 :(得分:6)

是的,你是对的:enum { value = true };并没有占用任何记忆。

此外,在C ++ 11之前,几乎只有 方式实现这一点:static const bool value = true;仅在C ++ 11以后的类定义中是合法的。虽然可能首选constexpr

答案 3 :(得分:0)

它也是包含它的每个目标文件中的另一个符号,没有任何好处。如果你使用符号折叠(--gc-sections),你将会用完可分离的部分&amp;膨胀你的二进制文件。

答案 4 :(得分:0)

有些人写的不太明显,enum而不是static bool const,因为他们没有意识到应该进行其他更改。

如果需要其地址,例如,如果将C ++传递给此函数foo,则需要定义该对象:

void foo(bool const &);

但是,通过定义对象解决问题实际上不是解决此问题的正确方法。以下是一些替代方法:

  1. Small objects should not be passed by reference。更改应该是从函数签名中删除const &为对象添加定义。

  2. 在无法更改函数签名的情况下,可以在调用中显式创建一个临时项:foo( bool { Cls::mbr } )

  3. 但是,这是编译时间信息!因此,foo应该是具有TT*重载的模板,或者应该是bool的专用模板。

该第三种解决方案的优点是消除了不必要的运行时检查(希望通过编译器进行优化),并且还允许独立处理指针和非指针情况,从而可能使代码更清晰。