静态常量字符串(类成员)

我想有一个类的私有静态常量(在这种情况下是一个形状工厂)。

我想要这样的东西。

class A {
private:
static const string RECTANGLE = "rectangle";
}

不幸的是,我从c++ (g++)编译器得到了各种各样的错误,比如:

ISO c++禁止初始化 成员“矩形”< / p >

非整型静态数据成员' std::string '的类内初始化无效

错误:使“矩形”静态

这说明这种构件设计是不符合标准的。如何在不使用#define指令的情况下获得私有文字常量(或者公共常量)(我想避免数据全局的丑陋!)

任何帮助都是感激的。

460837 次浏览

你必须在类定义之外定义你的静态成员,并在那里提供初始化式。

第一个

// In a header file (if it is in a header file in your case)
class A {
private:
static const string RECTANGLE;
};

然后

// In one of the implementation files
const string A::RECTANGLE = "rectangle";

您最初尝试使用的语法(类定义中的初始化式)只允许整型和枚举类型。


从c++ 17开始,你有另一个选项,这与你最初的声明非常相似:内联变量

// In a header file (if it is in a header file in your case)
class A {
private:
inline static const string RECTANGLE = "rectangle";
};

不需要额外的定义。

使用类内初始化 语法中,常量必须是静态的 整型或枚举类型的常量 由常量表达式初始化

这就是限制条件。因此,在这种情况下,您需要在类之外定义变量。引用@AndreyT的答案

这只是额外的信息,但如果你真的想在头文件中使用字符串,可以尝试如下方法:

class foo
{
public:
static const std::string& RECTANGLE(void)
{
static const std::string str = "rectangle";


return str;
}
};

尽管我怀疑这是否被推荐。

当前标准只允许对静态常量整型进行这样的初始化。所以你需要按照AndreyT解释的去做。然而,这将在下一个标准中通过新的成员初始化语法提供。

在类定义内部,只能声明静态成员。它们必须是类外部的定义。对于编译时整型常量,标准给出了可以“初始化”成员的例外。但它仍然不是一个定义。例如,如果没有定义,就不能使用地址。

我想提一下,我没有看到使用std::string而不是const char[] 为常量的好处。string很好,但它需要动态初始化。所以,如果你写

const std::string foo = "hello";

在命名空间范围内,foo的构造函数将在main开始执行之前运行,该构造函数将在堆内存中创建常量“hello”的副本。除非你真的需要RECTANGLE作为std::string,否则你也可以这样写

// class definition with incomplete static member could be in a header file
class A {
static const char RECTANGLE[];
};


// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";

在那里!没有堆分配,没有复制,没有动态初始化。

干杯,年代。

可能的只是做:

static const std::string RECTANGLE() const {
return "rectangle";
}

#define RECTANGLE "rectangle"
你可以选择上面提到的const char*解决方案,但如果你一直需要字符串,你会有很多开销 另一方面,静态字符串需要动态初始化,因此如果你想在另一个全局/静态变量初始化期间使用它的值,你可能会遇到初始化顺序的问题。为了避免这种情况,最便宜的方法是通过getter访问静态字符串对象,getter检查对象是否已初始化。< / p >
//in a header
class A{
static string s;
public:
static string getS();
};
//in implementation
string A::s;
namespace{
bool init_A_s(){
A::s = string("foo");
return true;
}
bool A_s_initialized = init_A_s();
}
string A::getS(){
if (!A_s_initialized)
A_s_initialized = init_A_s();
return s;
}

记住只使用A::getS()。因为任何线程只能由main()启动,并且A_s_initializedmain()之前初始化,所以即使在多线程环境中也不需要锁。A_s_initialized默认为0(在动态初始化之前),因此如果在s初始化之前使用getS(),则可以安全地调用init函数。

顺便说一句,在上面的答案:“std::string矩形()常量”中,静态函数不能为const,因为它们不能改变任何对象的状态(没有this指针)。

在c++ 11中,你现在可以做到:

class A {
private:
static constexpr const char* STRING = "some useful string constant";
};

类静态变量可以在头文件中为宣布,但在.cpp文件中必须为定义。这是因为一个静态变量只能有一个实例,而编译器无法决定将它放在哪个生成的对象文件中,所以你必须做出决定。

保留c++ 11中声明的静态值定义 可以使用嵌套的静态结构。在本例中是static成员 是一个结构,必须在.cpp文件中定义,但值

class A
{
private:
static struct _Shapes {
const std::string RECTANGLE {"rectangle"};
const std::string CIRCLE {"circle"};
} shape;
};

不是初始化单个成员,而是在.cpp中初始化整个静态结构:

A::_Shapes A::shape;

访问这些值

A::shape.RECTANGLE;

或者——因为成员是私有的,只能从A - with中使用

shape.RECTANGLE;

注意这个解决方案仍然存在的顺序问题 静态变量的初始化。当使用静态值时 初始化另一个静态变量,第一个可能没有初始化, 然而。< / p >

// file.h
class File {
public:
static struct _Extensions {
const std::string h{ ".h" };
const std::string hpp{ ".hpp" };
const std::string c{ ".c" };
const std::string cpp{ ".cpp" };
} extension;
};


// file.cpp
File::_Extensions File::extension;


// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };
在这种情况下,静态变量将包含{""} 或{".h", ".hpp"},取决于链接器创建的初始化顺序

正如@abyss提到的。7如果变量的值可以在编译时计算,你也可以使用constexpr。但是如果你用static constexpr const char*声明你的字符串并且你的程序使用std::string,否则就会有开销,因为每次你使用这样的常量都会创建一个新的std::string对象:

class A {
public:
static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
foo(A::STRING); // a new std::string is constructed and destroyed.
}

在c++ 17中,你可以使用内联变量:

class A {
private:
static inline const std::string my_string = "some useful string constant";
};

注意,这与深渊。7的答案不同:它定义了一个实际的std::string对象,而不是const char*对象

快进到2018年和c++ 17。

  • 不要使用std::string,使用std::string_view字面量
  • 请注意“constexpr”的咆哮。这也是一种“编译时”机制。
  • 没有内联并不意味着重复
  • 不需要CPP文件
  • static_assert 'works' only在编译时

    using namespace std::literals;
    
    
    namespace STANDARD {
    constexpr
    inline
    auto
    compiletime_static_string_view_constant() {
    // make and return string view literal
    // will stay the same for the whole application lifetime
    // will exhibit standard and expected interface
    // will be usable at both
    // runtime and compile time
    // by value semantics implemented for you
    auto when_needed_ =  "compile time"sv;
    return when_needed_  ;
    }
    

    < / p >};

以上是一个适当的、合法的标准c++公民。它可以很容易地涉及到任何和所有std::算法、容器、实用程序等。例如:

// test the resilience
auto return_by_val = []() {
auto return_by_val = []() {
auto return_by_val = []() {
auto return_by_val = []() {
return STANDARD::compiletime_static_string_view_constant();
};
return return_by_val();
};
return return_by_val();
};
return return_by_val();
};


// actually a run time
_ASSERTE(return_by_val() == "compile time");


// compile time
static_assert(
STANDARD::compiletime_static_string_view_constant()
== "compile time"
);

享受标准c++吧