如何在头文件中声明 static const char* ?

发布于 2024-08-09 17:00:00 字数 300 浏览 5 评论 0原文

我想在我的头文件中定义一个常量 char* 供我的 .cpp 文件使用。所以我尝试了这个:

private:
    static const char *SOMETHING = "sommething";

这给我带来了以下编译器错误:

错误 C2864:“SomeClass::SOMETHING”: 仅静态常量整型数据 成员可以在一个 类

我是 C++ 新手。这是怎么回事?为什么这是非法的?或者你可以怎样做呢?

I'd like to define a constant char* in my header file for my .cpp file to use. So I've tried this:

private:
    static const char *SOMETHING = "sommething";

Which brings me with the following compiler error:

error C2864: 'SomeClass::SOMETHING' :
only static const integral data
members can be initialized within a
class

I'm new to C++. What is going on here? Why is this illegal? And how can you do it alternatively?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

孤独难免 2024-08-16 17:00:00

注意:自 C++11 以来,这已经发生了变化,也请阅读其他答案

您需要在翻译单元中定义静态变量,除非它们是整型。

在您的标头中:

private:
    static const char *SOMETHING;
    static const int MyInt = 8; // would be ok

在 .cpp 文件中:

const char *YourClass::SOMETHING = "something";

C++ 标准,9.4.2/4:

如果静态数据成员是 const
整型或常量枚举类型,
它在类中的声明
定义可以指定一个
常量初始化器应为
积分常量表达式。在那
在这种情况下,该成员可以出现在
内的整数常量表达式
它的范围。该会员仍应
定义在命名空间范围内(如果是)
在程序和命名空间中使用
范围定义不应包含
初始化器。

NB : this has changed since C++11, read other answers too

You need to define static variables in a translation unit, unless they are of integral types.

In your header:

private:
    static const char *SOMETHING;
    static const int MyInt = 8; // would be ok

In the .cpp file:

const char *YourClass::SOMETHING = "something";

C++ standard, 9.4.2/4:

If a static data member is of const
integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression. In that
case, the member can appear in
integral constant expressions within
its scope. The member shall still be
defined in a namespace scope if it is
used in the program and the namespace
scope definition shall not contain an
initializer.

只想待在家 2024-08-16 17:00:00

使用 C++11,您可以使用 constexpr 关键字并在标头中写入:

private:
    static constexpr const char* SOMETHING = "something";

注释:

  • constexpr 使 SOMETHING 成为常量指针,因此您无法编写

    SOMETHING = "不同的东西";
    

    稍后。

  • 根据您的编译器,您可能还需要在 .cpp 文件中编写显式定义:

    constexpr const char* MyClass::SOMETHING;
    

With C++11 you can use the constexpr keyword and write in your header:

private:
    static constexpr const char* SOMETHING = "something";

Notes:

  • constexpr makes SOMETHING a constant pointer so you cannot write

    SOMETHING = "something different";
    

    later on.

  • Depending on your compiler, you might also need to write an explicit definition in the .cpp file:

    constexpr const char* MyClass::SOMETHING;
    
不奢求什么 2024-08-16 17:00:00

回答OP关于为什么只允许使用整数类型的问题。

当一个对象用作左值(即作为在存储中具有地址的对象)时,它必须满足“一个定义规则”(ODR),即它必须在一个且仅一个翻译单元中定义。编译器不能也不会决定在哪个翻译单元中定义该对象。这是您的责任。通过在某处定义该对象,您不仅仅是在定义它,您实际上是在告诉编译器您想要在这里定义它,在这个特定的地方翻译单位。

同时,在C++语言中整型常量具有特殊的地位。它们可以形成积分常量表达式(ICE)。在 ICE 中,整型常量被用作普通,而不是对象(即,此类整型值在存储中是否有地址并不相关)。事实上,ICE 是在编译时评估的。为了促进积分常量的使用,它们的值必须是全局可见的。并且常量本身并不需要在存储中占据实际位置。由于这种整型常量受到特殊待遇:允许在头文件中包含它们的初始化程序,并且放宽了提供定义的要求(首先是事实上的,然后是法律上的)。

其他常量类型没有这样的属性。其他常量类型实际上总是用作左值(或者至少不能参与 ICE 或类似于 ICE 的任何内容),这意味着它们需要定义。其余的如下。

To answer the OP's question about why it is only allowed with integral types.

When an object is used as an lvalue (i.e. as something that has address in storage), it has to satisfy the "one definition rule" (ODR), i.e it has to be defined in one and only one translation unit. The compiler cannot and will not decide which translation unit to define that object in. This is your responsibility. By defining that object somewhere you are not just defining it, you are actually telling the compiler that you want to define it here, in this specific translation unit.

Meanwhile, in C++ language integral constants have special status. They can form integral constant expressions (ICEs). In ICEs integral constants are used as ordinary values, not as objects (i.e. it is not relevant whether such integral value has address in the storage or not). In fact, ICEs are evaluated at compile time. In order to facilitate such a use of integral constants their values have to be visible globally. And the constant itself don't really need an actual place in the storage. Because of this integral constants received special treatment: it was allowed to include their initializers in the header file, and the requirement to provide a definition was relaxed (first de facto, then de jure).

Other constant types has no such properties. Other constant types are virtually always used as lvalues (or at least can't participate in ICEs or anything similar to ICE), meaning that they require a definition. The rest follows.

凹づ凸ル 2024-08-16 17:00:00

错误是您无法在类中初始化 static const char*。您只能在那里初始化整数变量。

你需要在类中声明成员变量,然后在类外对其进行初始化:

// 头文件

class Foo {
    static const char *SOMETHING;
    // rest of class
};

// cpp 文件

const char *Foo::SOMETHING = "sommething";

如果这看起来很烦人,可以认为这是因为初始化只能出现在一个翻译单元中。如果它在类定义中,通常会包含在多个文件中。常量整数是一种特殊情况(这意味着错误消息可能并不那么清晰),编译器可以有效地用整数值替换变量的使用。

相比之下,char*变量指向内存中的实际对象,该对象需要真正存在,并且是定义(包括初始化)使该对象存在。 “一个定义规则”意味着您不想将其放入标头中,因为这样包括该标头的所有翻译单元都将包含该定义。它们无法链接在一起,即使字符串中包含相同的字符,因为根据当前的 C++ 规则,您定义了两个具有相同名称的不同对象,这是不合法的。事实上,它们碰巧具有相同的字符并不意味着它是合法的。

The error is that you cannot initialize a static const char* within the class. You can only initialize integer variables there.

You need to declare the member variable in the class, and then initialize it outside the class:

// header file

class Foo {
    static const char *SOMETHING;
    // rest of class
};

// cpp file

const char *Foo::SOMETHING = "sommething";

If this seems annoying, think of it as being because the initialization can only appear in one translation unit. If it was in the class definition, that would usually be included by multiple files. Constant integers are a special case (which means the error message perhaps isn't as clear as it might be), and compilers can effectively replace uses of the variable with the integer value.

In contrast, a char* variable points to an actual object in memory, which is required to really exist, and it's the definition (including initialization) which makes the object exist. The "one definition rule" means you therefore don't want to put it in a header, because then all translation units including that header would contain the definition. They could not be linked together, even though the string contains the same characters in both, because under current C++ rules you've defined two different objects with the same name, and that's not legal. The fact that they happen to have the same characters in them doesn't make it legal.

风铃鹿 2024-08-16 17:00:00
class A{
public:
   static const char* SOMETHING() { return "something"; }
};

我一直这样做——特别是对于昂贵的 const 默认参数。

class A{
   static
   const expensive_to_construct&
   default_expensive_to_construct(){
      static const expensive_to_construct xp2c(whatever is needed);
      return xp2c;
   }
};
class A{
public:
   static const char* SOMETHING() { return "something"; }
};

I do it all the time - especially for expensive const default parameters.

class A{
   static
   const expensive_to_construct&
   default_expensive_to_construct(){
      static const expensive_to_construct xp2c(whatever is needed);
      return xp2c;
   }
};
风柔一江水 2024-08-16 17:00:00

如果您使用的是 Visual C++,则可以使用链接器的提示以不可移植的方式执行此操作...

// In foo.h...

class Foo
{
public:
   static const char *Bar;
};

// Still in foo.h; doesn't need to be in a .cpp file...

__declspec(selectany)
const char *Foo::Bar = "Blah";

__declspec(selectany) 意味着即使 Foo::Bar 将get 在多个目标文件中声明,链接器只会选取一个。

请记住,这仅适用于 Microsoft 工具链。不要指望它是便携式的。

If you're using Visual C++, you can non-portably do this using hints to the linker...

// In foo.h...

class Foo
{
public:
   static const char *Bar;
};

// Still in foo.h; doesn't need to be in a .cpp file...

__declspec(selectany)
const char *Foo::Bar = "Blah";

__declspec(selectany) means that even though Foo::Bar will get declared in multiple object files, the linker will only pick up one.

Keep in mind this will only work with the Microsoft toolchain. Don't expect this to be portable.

有一个技巧可以与模板一起使用来提供仅 H 文件常量。

(注意,这是一个丑陋的例子,但至少在 g++ 4.6.1 中逐字工作。)

(values.hpp 文件)

#include <string>

template<int dummy>
class tValues
{
public:
   static const char* myValue;
};

template <int dummy> const char* tValues<dummy>::myValue = "This is a value";

typedef tValues<0> Values;

std::string otherCompUnit(); // test from other compilation unit

(main.cpp)

#include <iostream>
#include "values.hpp"

int main()
{
   std::cout << "from main: " << Values::myValue << std::endl;
   std::cout << "from other: " << otherCompUnit() << std::endl;
}

(other.cpp)

#include "values.hpp"

std::string otherCompUnit () {
   return std::string(Values::myValue);
}

编译(例如 g++ -o main main.cpp other.) cpp && ./main) 并看到两个编译单元引用标头中声明的相同常量:

from main: This is a value
from other: This is a value

在 MSVC 中,您可以使用 __declspec(selectany)

例如:

__declspec(selectany) const char* data = "My data";

There is a trick you can use with templates to provide H file only constants.

(note, this is an ugly example, but works verbatim in at least in g++ 4.6.1.)

(values.hpp file)

#include <string>

template<int dummy>
class tValues
{
public:
   static const char* myValue;
};

template <int dummy> const char* tValues<dummy>::myValue = "This is a value";

typedef tValues<0> Values;

std::string otherCompUnit(); // test from other compilation unit

(main.cpp)

#include <iostream>
#include "values.hpp"

int main()
{
   std::cout << "from main: " << Values::myValue << std::endl;
   std::cout << "from other: " << otherCompUnit() << std::endl;
}

(other.cpp)

#include "values.hpp"

std::string otherCompUnit () {
   return std::string(Values::myValue);
}

Compile (e.g. g++ -o main main.cpp other.cpp && ./main) and see two compilation units referencing the same constant declared in a header:

from main: This is a value
from other: This is a value

In MSVC, you may instead be able to use __declspec(selectany)

For example:

__declspec(selectany) const char* data = "My data";
只有一腔孤勇 2024-08-16 17:00:00

C++ 标准仅允许对整型或枚举类型使用常量初始值设定项。详情参见9.4.2/4:

如果静态数据成员是 const 整型或 const 枚举类型,则其在类中的声明
定义可以指定一个常量初始化器,它应该是一个整型常量表达式(5.19)。在那
在这种情况下,成员可以出现在整型常量表达式中。该成员仍须以名称界定 ——
空间范围(如果在程序中使用)并且名称空间范围定义不应包含初始值设定项。

和 9.4.2/7:

静态数据成员的初始化和销毁​​与非本地对象完全相同(3.6.2、3.6.3)。

所以你应该在cpp文件中的某处写入:

const char* SomeClass::SOMETHING = "sommething";

Constant initializer allowed by C++ Standard only for integral or enumeration types. See 9.4.2/4 for details:

If a static data member is of const integral or const enumeration type, its declaration in the class
definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that
case, the member can appear in integral constant expressions. The member shall still be defined in a name-
space scope if it is used in the program and the namespace scope definition shall not contain an initializer.

And 9.4.2/7:

Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).

So you should write somewhere in cpp file:

const char* SomeClass::SOMETHING = "sommething";
电影里的梦 2024-08-16 17:00:00

为了回答为什么这个问题,整型类型很特殊,因为它们不是对已分配对象的引用,而是重复和复制的值。这只是定义语言时做出的实现决策,即以尽可能高效和“内联”的方式处理对象系统外部的值。

这并不能完全解释为什么它们被允许作为类型中的初始值设定项,但可以将其视为本质上的 #define,然后它将作为类型的一部分而不是对象的一部分有意义。

To answer the why question, integral types are special in that they are not a reference to an allocated object but rather values that are duplicated and copied. It's just an implementation decision made when the language was defined, which was to handle values outside the object system and in as efficient and "inline" a fashion as possible.

This doesn't exactly explain why they are allowed as initializors in a type, but think of it as essentially a #define and then it will make sense as part of the type and not part of the object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文