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

lb.*_*lb. 429 c++ string static const class

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

我想要有类似的东西.

class A {
   private:
      static const string RECTANGLE = "rectangle";
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我从C++(g ++)编译器中得到了各种错误,例如:

ISO C++禁止成员'RECTANGLE'的初始化

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

错误:使'RECTANGLE'静止

这告诉我这种成员设计不符合标准.如何在不使用#define指令的情况下拥有私有文字常量(或者可能是公共的)(我想避免数据全局性的丑陋!)

任何帮助表示赞赏.

AnT*_*AnT 451

您必须在类定义之外定义静态成员并在那里提供初始化程序.

第一

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

然后

// In one of the implementation files
const string A::RECTANGLE = "rectangle";
Run Code Online (Sandbox Code Playgroud)

您最初尝试使用的语法(类定义中的初始化程序)仅允许使用整数和枚举类型.


从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";
};
Run Code Online (Sandbox Code Playgroud)

无需额外定义.

  • 我不确定它总是减少开销 - 这取决于使用情况.如果要将此成员作为参数传递给带有const字符串&的函数,则在初始化期间将为每个调用创建临时创建的一个字符串对象.用于创建静态字符串对象的IMHO开销可以忽略不计. (50认同)
  • 我宁愿一直使用std :: string.开销可以忽略不计,但你有更多的选择,并且更不可能写一些愚蠢的东西,比如"魔术"== A :: RECTANGLE只是为了比较他们的地址...... (22认同)
  • `char const*`具有在完成所有动态初始化之前初始化的优点.因此,在任何对象的构造函数中,您可以依赖于"RECTANGLE"来进行初始化. (9认同)
  • 此外,如果不需要使用STL字符串,您也可以只定义一个const char*.(减少开销) (7认同)
  • @cirosantilli:因为从C++开始,初始化者是*定义*的一部分,而不是*声明*.并且类中的数据成员声明就是:声明.(另一方面,对于const integral和enum成员,以及在C++ 11中,对于*literal*类型的const成员进行了例外处理.) (7认同)
  • *从 C++20 开始,您可以将其声明为 constexpr,而不是 const* - 不。在 C++20 中,`std::string` 不能是 `constexpr`。它可以在 `constexpr` 函数中使用,但不能用作 `constexpr` 变量。请参阅[godbolt 链接](https://gcc.godbolt.org/z/Gf96hsz8d) (3认同)
  • 为什么c ++不允许在类中初始化?会导致歧义或其他问题吗? (2认同)
  • @cirosantilli:http://stackoverflow.com/questions/13697265/static-const-double-cannot-have-an-in-class-initializer-why-is-it-so (2认同)

aby*_*s.7 147

在C++ 11中,您现在可以做到:

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这个解决方案不适用于std :: string. (28认同)
  • 请注意1.这仅适用于文字,而2.不符合标准,尽管Gnu / GCC遵守罚款规定,但其他编译器将引发错误。定义必须真实。 (2认同)
  • @ ManuelSchneid3r这个"不符合标准"究竟是什么?看起来像bog标准的C++ 11 _brace-or-equal initialisation_给我. (2认同)
  • @rvighne,不,这不对.`constexpr`意味着var的`const`,而不是它指向的类型.即`static constexpr const char*const`与`static constexpr const char*`相同,但与`static constexpr char*`不同. (2认同)
  • @ abyss.7 - 谢谢你的回答,我有另一个请:为什么它必须是静态的? (2认同)

sel*_*tze 33

在类定义中,您只能声明静态成员.它们必须在课堂之外定义.对于编译时积分常量,标准会使您可以"初始化"成员.但它仍然不是一个定义.例如,如果没有定义,那么获取地址是行不通的.

我想提一提,我没有看到使用的std :: string在为const char []的利益为常数.std :: string很好,除了它需要动态初始化.所以,如果你写的东西像

const std::string foo = "hello";
Run Code Online (Sandbox Code Playgroud)

在命名空间范围内,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";
Run Code Online (Sandbox Code Playgroud)

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

干杯,s.

  • C++11 没有 std::string_view。 (5认同)

GMa*_*ckG 17

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

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

        return str;
    }
};
Run Code Online (Sandbox Code Playgroud)

虽然我怀疑这是推荐的.

  • 我不会推荐它.我经常这样做.它工作正常,我发现它比将字符串放在实现文件中更明显.std :: string的实际数据仍然位于堆上.我将返回一个const char*,在这种情况下,您不需要声明静态变量,因此声明将占用更少的空间(代码明智).只是品味问题. (4认同)

Oz *_*mon 13

在C++ 17中,您可以使用内联变量:

class A {
 private:
  static inline const std::string my_string = "some useful string constant";
};
Run Code Online (Sandbox Code Playgroud)

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

  • @shuva不,[变量不会重复](/sf/ask/3162832711/)。 (2认同)

aJ.*_*aJ. 8

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

这是限制.因此,在这种情况下,您需要在类外定义变量.请参阅@AndreyT的回答


Mar*_*nič 7

类静态变量可以在标头中声明,但必须在.cpp文件中定义。这是因为静态变量只能有一个实例,并且编译器无法决定将其放置在哪个生成的目标文件中,因此您必须做出决定。

为了用C ++ 11中的声明保留静态值的定义,可以使用嵌套的静态结构。在这种情况下,静态成员是一个结构,必须在.cpp文件中定义,但值在标头中。

class A
{
private:
  static struct _Shapes {
     const std::string RECTANGLE {"rectangle"};
     const std::string CIRCLE {"circle"};
  } shape;
};
Run Code Online (Sandbox Code Playgroud)

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

A::_Shapes A::shape;
Run Code Online (Sandbox Code Playgroud)

通过以下方式访问值

A::shape.RECTANGLE;
Run Code Online (Sandbox Code Playgroud)

或-由于成员是私有成员,并且只能从A使用-

shape.RECTANGLE;
Run Code Online (Sandbox Code Playgroud)

注意,该解决方案仍然遭受静态变量的初始化顺序的问题。当使用静态值初始化另一个静态变量时,第一个静态变量可能尚未初始化。

// 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 };
Run Code Online (Sandbox Code Playgroud)

在这种情况下,静态变量将包含{“”}或{“ .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.
}
Run Code Online (Sandbox Code Playgroud)


小智 5

快进到 2018 年和 C++17。

  • 不要使用 std::string,使用 std::string_view 文字
  • 请注意下面的“constexpr”。这也是一种“编译时”机制。
  • 没有内联并不意味着重复
  • 为此不需要 cpp 文件
  • 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_  ;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    };

以上是一个正当的、合法的标准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" 
 );
Run Code Online (Sandbox Code Playgroud)

享受标准 C++