简体   繁体   English

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

[英]Static constant string (class member)

I'd like to have a private static constant for a class (in this case a shape-factory).我想要一个类的私有静态常量(在本例中是一个形状工厂)。

I'd like to have something of the sort.我想要那种东西。

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

Unfortunately I get all sorts of error from the C++ (g++) compiler, such as:不幸的是,我从 C++ (g++) 编译器中得到了各种错误,例如:

ISO C++ forbids initialization of member 'RECTANGLE' ISO C++ 禁止初始化成员“RECTANGLE”

invalid in-class initialization of static data member of non-integral type 'std::string'非整数类型“std::string”的静态数据成员的类内初始化无效

error: making 'RECTANGLE' static错误:使“矩形”静态

This tells me that this sort of member design is not compliant with the standard.这告诉我这种成员设计不符合标准。 How do you have a private literal constant (or perhaps public) without having to use a #define directive (I want to avoid the uglyness of data globality!)您如何在不必使用#define 指令的情况下拥有私有文字常量(或者可能是公共的)(我想避免数据全局性的丑陋!)

Any help is appreciated.任何帮助表示赞赏。

You have to define your static member outside the class definition and provide the initializer there.您必须在类定义之外定义静态成员并在那里提供初始化程序。

First第一的

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

and then接着

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

The syntax you were originally trying to use (initializer inside class definition) is only allowed with integral and enum types.您最初尝试使用的语法(类定义中的初始化程序)仅允许用于整数和枚举类型。


Starting from C++17 you have another option, which is quite similar to your original declaration: inline variables从 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";
};

No additional definition is needed.不需要额外的定义。

In C++11 you can do now:在 C++11 中,您现在可以执行以下操作:

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

Inside class definitions you can only declare static members.在类定义中,您只能声明静态成员。 They have to be defined outside of the class.它们必须在类之外定义 For compile-time integral constants the standard makes the exception that you can "initialize" members.对于编译时整数常量,标准规定您可以“初始化”成员。 It's still not a definition, though.不过,这仍然不是一个定义。 Taking the address would not work without definition, for example.例如,如果没有定义,获取地址是行不通的。

I'd like to mention that I don't see the benefit of using std::string over const char[] for constants .我想提一下,我看不到使用 std::string 而不是 const char[] for constants的好处。 std::string is nice and all but it requires dynamic initialization. std::string 很好,但它需要动态初始化。 So, if you write something like所以,如果你写类似

const std::string foo = "hello";

at namespace scope the constructor of foo will be run right before execution of main starts and this constructor will create a copy of the constant "hello" in the heap memory.在命名空间范围内,foo 的构造函数将在 main 开始执行之前运行,并且此构造函数将在堆内存中创建常量“hello”的副本。 Unless you really need RECTANGLE to be a std::string you could just as well write除非你真的需要 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";

There!那里! No heap allocation, no copying, no dynamic initialization.没有堆分配,没有复制,没有动态初始化。

Cheers, s.干杯,S。

In C++ 17 you can use inline variables :在 C++ 17 中,您可以使用内联变量

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

Note that this is different from abyss.7's answer : This one defines an actual std::string object, not a const char*请注意,这与abyss.7 的答案不同:这个定义了一个实际的std::string对象,而不是const char*

This is just extra information, but if you really want the string in a header file, try something like:这只是额外的信息,但如果您真的想要头文件中的字符串,请尝试以下操作:

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

        return str;
    }
};

Though I doubt that's recommended.虽然我怀疑这是推荐的。

The class static variables can be declared in the header but must be defined in a .cpp file.类静态变量可以在标头中声明,但必须在 .cpp 文件中定义 This is because there can be only one instance of a static variable and the compiler can't decide in which generated object file to put it so you have to make the decision, instead.这是因为静态变量只能有一个实例,编译器无法决定将它放在哪个生成的目标文件中,因此您必须做出决定。

To keep the definition of a static value with the declaration in C++11 a nested static structure can be used.为了在 C++11 中的声明中保留静态值的定义,可以使用嵌套的静态结构。 In this case the static member is a structure and has to be defined in a .cpp file, but the values are in the header.在这种情况下,静态成员是一个结构,必须在 .cpp 文件中定义,但值在标头中。

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

Instead of initializing individual members the whole static structure is initialized in .cpp:整个静态结构不是初始化单个成员,而是在 .cpp 中初始化:

A::_Shapes A::shape;

The values are accessed with这些值通过以下方式访问

A::shape.RECTANGLE;

or -- since the members are private and are meant to be used only from A -- with或 -- 因为成员是私有的,并且只能从 A 中使用 -- 与

shape.RECTANGLE;

Note that this solution still suffers from the problem of the order of initialization of the static variables.请注意,此解决方案仍然存在静态变量初始化顺序的问题。 When a static value is used to initialize another static variable, the first may not be initialized, yet.当使用静态值初始化另一个静态变量时,第一个静态变量可能尚未初始化。

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

In this case the static variable headers will contain either { "" } or { ".h", ".hpp" }, depending on the order of initialization created by the linker.在这种情况下,静态变量将包含 { "" } 或 { ".h", ".hpp" },具体取决于链接器创建的初始化顺序。

As mentioned by @abyss.7 you could also use constexpr if the value of the variable can be computed at compile time.正如@abyss.7 所述,如果可以在编译时计算变量的值,您也可以使用constexpr But if you declare your strings with static constexpr const char* and your program uses std::string otherwise there will be an overhead because a new std::string object will be created every time you use such a constant:但是如果你用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.
}

To use that in-class initialization syntax, the constant must be a static const of integral or enumeration type initialized by a constant expression.要使用该类内初始化语法,常量必须是由常量表达式初始化的整型或枚举类型的静态常量。

This is the restriction.这是限制。 Hence, in this case you need to define variable outside the class.因此,在这种情况下,您需要在类之外定义变量。 refer answwer from @AndreyT参考@AndreyT的答案

You can either go for the const char* solution mentioned above, but then if you need string all the time, you're going to have a lot of overhead.您可以选择上面提到的const char*解决方案,但是如果您一直需要字符串,那么您将有很多开销。
On the other hand, static string needs dynamic initialization, thus if you want to use its value during another global/static variable's initialization, you might hit the problem of initialization order.另一方面,静态字符串需要动态初始化,因此如果你想在另一个全局/静态变量的初始化过程中使用它的值,你可能会遇到初始化顺序的问题。 To avoid that, the cheapest thing is accessing the static string object through a getter, which checks if your object is initialized or not.为避免这种情况,最便宜的方法是通过 getter 访问静态字符串对象,它会检查您的对象是否已初始化。

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

Remember to only use A::getS() .请记住只使用A::getS() Because any threading can only started by main() , and A_s_initialized is initialized before main() , you don't need locks even in a multithreaded environment.因为任何线程只能由main()启动,并且A_s_initializedmain()之前初始化,所以即使在多线程环境中也不需要锁。 A_s_initialized is 0 by default (before the dynamic initialization), so if you use getS() before s is initialized, you call the init function safely. A_s_initialized默认为 0(在动态初始化之前),因此如果在 s 初始化之前使用getS() ,则可以安全地调用 init 函数。

Btw, in the answer above: " static const std::string RECTANGLE() const " , static functions cannot be const because they cannot change the state if any object anyway (there is no this pointer).顺便说一句,在上面的答案中:“ static const std::string RECTANGLE() const ”,静态函数不能是const ,因为如果有任何对象(没有 this 指针),它们就不能改变状态。

possible just do:可能只是这样做:

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

or或者

#define RECTANGLE "rectangle"

The current standard only allows such initialization for static constant integral types.当前标准仅允许对静态常量整数类型进行此类初始化。 So you need to do as AndreyT explained.因此,您需要按照 AndreyT 的解释进行操作。 However, that will be available in the next standard through the new member initialization syntax .但是,这将在下一个标准中通过新的成员初始化语法提供。

Fast forward to 2018 and C++17.快进到 2018 年和 C++17。

  • do not use std::string, use std::string_view literals不要使用 std::string,使用 std::string_view 文字
  • please do notice the 'constexpr' bellow.请注意下面的“constexpr”。 This is also an "compile time" mechanism.这也是一种“编译时”机制。
  • no inline does not mean repetition没有内联并不意味着重复
  • no cpp files are not necessary for this为此不需要任何 cpp 文件
  • static_assert 'works' at compile time only static_assert 仅在编译时“有效”

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

    }; };

Above is a proper and legal standard C++ citizen.以上是一个适当且合法的标准 C++ 公民。 It can get readily involved in any and all std:: algorithms, containers, utilities and a such.它可以很容易地参与任何和所有 std:: 算法、容器、实用程序等。 For example:例如:

// 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" 
 );

Enjoy the standard C++享受标准 C++

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM