静态初始化顺序和字符串的连接

时间:2014-01-03 09:13:04

标签: c++ string string-concatenation static-initialization

我们有一个相当大的项目,在几个地方定义static const std::string作为参数名称;其中一些需要在静态初始化期间连接:

foo.h中:

struct Foo {
  static const std::string ParamSuffix;
};

Foo.cpp中:

const std::string Foo::ParamSuffix = ".suffix";

bar.h:

struct Bar {
  static const std::string ParamPrefix;
};

bar.cpp:

const std::string Bar::ParamPrefix = "prefix";

baz.h:

struct Baz {
    static const std::string ParamName;
};

baz.cpp:

const std::string Baz::ParamName = Bar::ParamPrefix + Foo::ParamSuffix;

问题显然是“静态初始化惨败”,因为未定义static const成员的初始化顺序。

我不喜欢通常的解决方案,即用函数替换所有这些变量,因为

  1. 有很多这些变量,即很多代码更改
  2. 连接需要特殊的功能,这使得代码库不一致,甚至更难看
  3. 我目前无法使用C ++ 11,这会使constexpr功能更加简单(我认为)。

    问题是:是否有任何技巧可以让我连接static const std::string(或包装对象或其他)以初始化另一个static const std::string

3 个答案:

答案 0 :(得分:4)

  

问题是:是否有任何技巧可以让我连接静态const std :: strings(或包装器对象或其他)来初始化另一个静态const std :: string?

除了你不喜欢的那个之外,不是一件简单的事情:为静态字符串创建函数。

虽然有一些非平凡的替代方案(例如,用字符串容器/字符串映射替换所有硬编码字符串并在应用程序启动时加载映射)。

我的建议是使用静态功能(你拒绝的解决方案)。

答案 1 :(得分:2)

为什么你认为std::string const有什么特别之处? (提示:它不是)即使您可以使用C ++ 11的constexpr它也无济于事,因为您无法使std:string成为常量表达式(它们可能需要分配内存,而不是'一个可行的常数表达式)。您可以处理用于constexpr的类似字符串的类,可以将其转换为std::string,但我不确定是否可以将它们连接起来以生成新的constexpr。< / p>

您可能要做的是提取您需要使用的字符串作为连接的一部分,并提供它们作为宏的值。字符串文字(适用于char const[N]的类型为N)可以通过简单地将它们放在一起来连接起来:

// foo.h
#define FOO_PARAM_SUFFIX ".suffix"
struct Foo {
    static const std::string ParamSuffix;
};

// foo.cpp:
std::string const Foo::ParamSuffix(FOO_PARAM_SUFFIX);

// bar.h:
#define BAR_PARAM_SUFFIX "prefix"
struct Bar {
    static std::string const ParamPrefix;
};

// bar.cpp:
std::string const Bar::ParamPrefix(BAR_PARAM_SUFFIX);

// baz.h:
#include "foo.h"
#include "bar.h"
#define BAZ_PARAM_NAME BAR_PARAM_PREFIX FOO_PARAM_SUFFIX

struct Baz {
    static std::string const ParamName;
};

// baz.cpp:
#include "foo.h"
#include "bar.h"
std::string const Baz::ParamName(BAR_PARAM_PREFIX FOO_PARAM_SUFFIX);

我猜想BAZ_PARAM_NAME没有在其他地方使用,在这种情况下它也不必定义:它被定义只是为了表明它可以完成。 Bar::ParamName的初始化假定BAZ_PARAM_NAME未定义。

答案 2 :(得分:2)

当文本不依赖于另一个变量时,您可以考虑将类型从const std::string更改为const char数组ala const char param_prefix[] = "prefix"; - 可能会减少对客户端使用的强制更改,但这可能会隐藏每次使用时必须(或无意中)创建的新临时std::string

检查你的系统,但你几乎可以肯定在构造其他const char常量时安全地使用这些std::string变量 - 但你不幸的是 - std::string依赖于其他std::string s。

如果要将现有值解释为可能的const - charstd::string仅仅取决于const - char太难了s,或者只是没有覆盖足够的情况,那么对每一个常数的批量修改都值得追求。

包装器的吸引力在于您可以保持使用的类型和语义相同。为此,您需要使用自己的类型替换所有这些运行时初始化的字符串,这些类型可以协调两个中心列表: - 初始化对象和等待对象。观察者模式是合适的,或者你可以例如注册回调列表并通过循环main()调用它们直到它们都表示成功来完成初始化:如果已知对象初始化中的至少一些是静态的,则可以允许一个对象测试另一个对象的初始化状态,将避免必须让构造函数注册完成。

如果可以借助更智能的源代码修改工具(例如awk)进行操作 - 最好只更改常量以便函数干净地返回。