【问题标题】:How to setup a global container (C++03)?如何设置全局容器(C++03)?
【发布时间】:2012-12-16 08:57:58
【问题描述】:

我想定义一个全局容器(C++03),这是我试过的一个示例代码,它不起作用。

#include <vector>
#include <string>
using namespace std;

vector<string> Aries;
Aries.push_back("Taurus");    // line 6

int main() {}

编译错误:

prog.cpp:6:1: error: 'Aries' does not name a type

看来我可以定义一个空的全局向量,但无法填充它。看起来在 C++03 中,我也不能指定初始化器,例如:

vector<string> Aries = { "Taurus" };

我在这里犯了一个错误,或者我该如何解决这个问题?

我曾尝试在 StackOverflow 上搜索以查看之前是否已回答过此问题,但只看到以下帖子:global objects in C++、Defining global constant in C++,但没有帮助回答此问题。

【问题讨论】:

标签: c++ global-variables containers c++03


【解决方案1】:

我找到了一个巧妙的解决方法来“初始化”C++03 全局 STL 容器(实际上是在 main() 之前“全局”执行代码)。这使用逗号运算符。见例子:

#include <vector>
#include <string>
#include <iostream>
using namespace std;

vector<string> Aries;

// dummy variable initialization to setup the vector.
// using comma operator here to cause code execution in global scope.
int dummy = (Aries.push_back("Taurus"), Aries.push_back("Leo"), 0);

int main() {
    cout << Aries.at(0) << endl;
    cout << Aries.at(1) << endl;
}

输出

Taurus
Leo

唯一真正的问题是额外的全局变量。

【讨论】:

  • 我很惊讶,但也对这个解决方案感到害怕。投赞成票;)
  • @milleniumbug 感谢您的支持,但您为什么害怕? ;)
  • 恕我直言,这对每个人来说都不是很明显,而且很容易混淆。我会在它之前发表评论来解释这个技巧。
  • 而且,对于全局变量问题 - 您可以将其声明为静态以避免外部链接,或者将其包裹在命名空间以避免污染全局范围。
  • @milleniumbug 啊,关于记录它的好点。让我编辑答案以包含它。
【解决方案2】:

虽然函数之外的声明和初始化(例如main)没有问题,但您不能在函数之外拥有代码。您要么需要在初始化时正确设置值(如在 C++11 中),要么在 main 中填充全局对象:

std::vector<string> Aries;

/* ... */

int main() {
    Aries.push_back("Taurus");
    /* ... */
}

其他方式(不填充main中的向量)

单值

还有其他方法不需要.push_back 或其他主要代码。例如,如果Aries 只包含一项,则可以将std::vector&lt;T&gt;::vector(size_t s, T t) 与s == 1 和t == "Taurus" 一起使用:

std::vector<string> Aries(1, "Taurus");

/* ... */

int main() { /* ... */ }

如果您多次需要相同的值,您可以简单地调整s。

多个不同的值

现在这有点棘手。您想使用合适的构造函数填充vector。 std::vector&lt;T&gt;在C++03中提供了四种不同的构造函数:

  1. std::vector&lt;T&gt;::vector()
  2. std::vector&lt;T&gt;::vector(size_t, T = T())
  3. template &lt;class InputIt&gt; std::vector&lt;T&gt;::vector(InputIt, InputIt)
  4. std::vector&lt;T&gt;::vector(const vector&amp;) 请注意,它们实际上都将分配器作为附加的最后一个参数,但这不是我们关心的问题。

我们可以忘记std::vector&lt;T&gt;::vector(),因为我们想用值填充向量,而std::vector&lt;T&gt;::vector(size_t, T) 也不适合,因为我们想要不同 值。剩下的是模板构造函数和复制构造函数。下面的例子展示了如何使用模板化的构造函数:

std::string const values[] = {"Taurus", "Ares", "Testos"};

template <class T, size_t N>
T* begin(T (&array)[N]){ // this is already in C++11, very helpful 
    return array;
}
template <class T, size_t N>
T* end(T (&array)[N]){
    return array+N;
}

std::vector<std::string> Aries(begin(values), end(values));

请注意,begin 和 end 不是必需的 - 我们可以简单地使用 Aries(values, values+3)。但是,事情往往会发生变化,您通常会添加一个值或删除一个值。如果您忘记更改3 的偏移量,您将忘记输入或跨越values 的边界。

但是,这个解决方案引入了一个新的全局变量,这不是很好。让我们退后一步。我们需要values,因为我们想使用std::vector&lt;T&gt;::vector(InputIt, InputIt),它需要一个有效的范围。并且范围在我们使用构造函数的时候必须是已知的,所以它需要是全局的。诅咒!但是请注意:我们的工具箱仍然包含一个构造函数,即复制构造函数。为了使用它,我们创建了一个附加函数:

namespace {
    std::vector<std::string> initializer(){
        const std::string dummy_array[] = {"Taurus", "Ares", "Testos"};
        return std::vector<std::string>(begin(dummy_array), end(dummy_array));
    }
}

std::vector<std::string> Aries(initializer());

此示例同时使用复制构造函数和范围构造函数。您还可以创建一个临时向量并使用std::vector&lt;T&gt;::push_back 将其填充到函数中。

【讨论】:

  • 什么,不想展示如何在 C++03 初始化时正确设置值?很容易做到...
  • @MooingDuck:嗯,对于单个值,可以只使用std::vector&lt;T&gt;::vector(size_t, T),但对于多个值,我们必须使用std::vector&lt;T&gt;::vector(InputIt, InputIt),因此需要额外使用const std::string [],还是我弄错了? std::vector&lt;T&gt;::vector(const &amp;vector) 可以通过一些辅助函数来实现。
  • 我总是直奔C++03中的辅助函数:简单易懂,处理所有情况。
【解决方案3】:

我的经验是,这种“惊人但可怕”(帽子提示)的解决方案是不可预测的。您的初始化在加载时运行。您无法保证加载 ORDER。因此,使用该容器的所有东西都必须在同一个模块中,或者以某种方式保证在访问容器之前加载该模块。否则,容器构造函数尚未运行,但您的代码会愉快地调用它的访问器。

当它出错时,它会出错,并且错误取决于编译器、平台和月相——在所有情况下,都没有提供关于南下的最遥远的线索。

【讨论】:

  • +1 用于提及该问题。但是,这并不特定于此解决方案。
  • 顺便说一句:“静态初始化顺序失败”的最佳解决方案是在不必要时停止使用具有外部链接的全局对象。
  • 您确实有一个好点,我知道静态初始化顺序惨败。然而,一个解决方案在 90% 的情况下是不安全的,并不意味着它不应该在其余 10% 的情况下安全使用。如果在不能解决手头问题的“始终安全”的解决方案和只有 10% 的时间安全但解决当前问题的解决方案之间进行选择,那么应该做什么就很清楚了。我知道您的意思不是说“永远不要这样做”,但为了让未来的读者清楚,不妨发表此评论。
【解决方案4】:

为了完整起见,此解决方案使用迭代器对构造函数和普通的旧数组初始值设定项。

#include <vector>
#include <string>
#include <iostream>
using namespace std;

string contents[] = {"Taurus", "Leo"};
vector<string> Aries(contents, contents + sizeof contents/sizeof contents[0]);

int main() {
    cout << Aries.at(0) << endl;
    cout << Aries.at(1) << endl;
}

这可能更清楚,但它调用了更多的复制构造函数。

【讨论】:

  • +1:因为Mooning Duck's comment,在我编辑我的之前没有注意到你的答案。这本可以为我节省几分钟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 2018-07-29
  • 1970-01-01
  • 2011-07-09
  • 1970-01-01
相关资源
最近更新 更多