【问题标题】:Using std::distance to initialise const members goes awry sometimes使用 std::distance 初始化 const 成员有时会出错
【发布时间】:2015-12-09 19:21:40
【问题描述】:

我对刚刚遇到的错误感到有些困惑。我正在使用 Visual Studio 2015,但我遇到了不一致的行为。我已经从它所在的项目中删除了代码并重新创建了行为,请允许我引导您完成代码:

template <typename Ty>
struct Simple_Array
{
    Ty* data;
    const size_t size;

    Simple_Array(size_t size) :
        size(size),
        data(new Ty[size])
    {
    }

    virtual ~Simple_Array() {
        delete[] data;
    }
};

如您所见,我在代码中为 c 数组使用了一个简单的包装器,我想使用 c 数组来进行自学,因此不使用 std::vector 或 std::array 之类的东西。在使用它时,我发现需要一个构造函数,该构造函数在任何其他容器上都采用第一个和最后一个迭代器,所以我决定使用 std::distance(first, last) 来计算大小。

    template <class It>
    Simple_Array(It first, It last) :
        size(std::distance(first, last)),
        data(new Ty[size])
    {
        for (int i = 0; first != last; ++first, ++i)
            data[i] = *first;
    }

现在,当我使用这个构造函数时,大小被初始化为疯狂的大值,例如 3435973836。为什么?这特别令人困惑,因为以下代码可以正常工作:

    template <class It>
    Simple_Array(It first, It last) :
        Simple_Array(std::distance(first, last))
    {
        for (int i = 0; first != last; ++first, ++i)
            data[i] = *first;
    }

为了测试这一点,我编写了以下主函数:

int main() {
    auto list = { 1, 2, 3, 4 };

    try {
        Simple_Array<int>(list.begin(), list.end());
    }
    catch (...) {
        std::cout << "Failed." << std::endl;
    }

    std::cin.get();

    return 0;
}

我看了一眼反汇编,但我还没有弄清楚问题出在哪里。好消息是因为我有一个解决方法,我不需要这个问题的答案来继续我的项目,但我仍然很想知道这里出了什么问题。

我将整个文件粘贴到这里:pastebin.com/ebNrsLaB

【问题讨论】:

  • 我回答了问题的原因。附带说明一下,您的第二个版本(带有委托构造函数的版本)实际上更好。
  • @SergeyA 是的,我同意你的观点。很高兴知道误解来自哪里!

标签: c++ arrays stl visual-studio-2015


【解决方案1】:

哇!这是我有生以来第一次真正看到臭名昭著的 gcc 警告中关于初始化顺序的一点。

特别是,类成员按照它们在类中定义的顺序进行初始化,而不是按照它们在构造函数初始化列表中列出的顺序。由于您的Ty* data; 是在size 之前定义的,因此在将大小设置为实际值之前对其进行初始化(分配) - 使用来自未初始化大小的随机值。

要解决此问题,请确保在类中的数据之前定义大小。

【讨论】:

  • 或者他们可以重命名参数,使其与类成员不同,然后使用参数而不是成员。
  • @NathanOliver,这显然是另一种选择。
  • 谢谢!我应该从一步一步中弄清楚,现在我考虑到一步一步是先初始化数据,然后是大小,但由于抛出异常而从未达到大小。所以我仍在阅读未初始化的大小。
  • @NathanOliver 好主意,根据 SergeyA 的建议,我将使用委托构造函数,因为它是设计构造函数的更好方法,但我会记住这一点。
【解决方案2】:
struct Simple_Array
{
    Ty* data;            // (1)
    const size_t size;   // (2)

    Simple_Array(size_t size) :
        size(size),         // (2)
        data(new Ty[size])  // (1)
    {
    }

成员变量按照它们在结构中声明的顺序进行初始化,不是按照您在构造函数初始化列表中的写入顺序。

编译器应该对此发出警告。

【讨论】:

  • MSVC 没有这个警告。
  • GCC 和英特尔 C++ 编译器应该这样做。
  • 我个人对这个警告的问题是它太笼统了。仅仅改变顺序通常是无害的。编译器至少应该做一个体面的尝试来确定顺序是否重要 - 它完全有能力这样做。
  • 有趣,奇怪的是 MSVC 没有警告你,谢谢!
猜你喜欢
  • 2019-01-16
  • 2015-10-09
  • 1970-01-01
  • 2020-10-07
  • 1970-01-01
  • 1970-01-01
  • 2015-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多