【问题标题】:Issue with bounds checking a member std::array inside a const function在 const 函数内检查成员 std::array 的问题
【发布时间】:2012-08-27 04:52:06
【问题描述】:

使用以下代码使用 mingw (gcc 4.7.0) 检查成员 std::array 时遇到奇怪的行为界限

#include <iostream>
#include <array>

class testClass
{
    std::array<int, 2> testArray;

    public:
        testClass();
        void func() const;

};

testClass::testClass() : testArray({{1, 2}})
{
}

void testClass::func() const
{
    for (int i = 0; i < 2; ++i)
        std::cout << testArray.at(i) << '\n' << testArray[i] << '\n';       
}


int main()
{
    testClass test;
    test.func();
}

输出是

0
1
0
2

该错误似乎与优化有关,因为它仅在使用-O 编译时才会出现,我尝试了-O 启用的各个标志,但无法进一步缩小范围。使函数非常量也解决了这个问题。这可能是一个错误还是我错过了什么?

*编辑

缩小范围,看起来像是.at()const 版本中的一个错误

#include <iostream>
#include <array>

int main()
{
    std::array<int, 2> const testArray = {1, 2};

    for (int i = 0; i < 2; ++i)
        std::cout << testArray.at(i) << '\n' << testArray[i] << '\n';       
}

在 Windows Xp sp3 和 Windows 7 sp1 上使用 mingw 4.7.0 使用 -std=c++11 -O 编译的相同输出。

*编辑 2

再次输出相同的输出

#include <iostream>
#include <array>

int main()
{
    typedef std::array<int, 2> Tarray;
    Tarray test = {1, 2};

    for (int i = 0; i < 2; ++i)
        std::cout << const_cast<Tarray const*>(&test)->at(i) << '\n' << test.at(i) << '\n';     
}

【问题讨论】:

  • 在我看来这绝对是个错误。
  • 它为我生成a different output...
  • g++ 4.8.0(实验性),所有代码仅适用于 -O0。与其他 -O 标志一起给出这样的警告:“ is used uninitialized in this function [-Wuninitialized]”
  • 我无法在 ubuntu linux 上使用 gcc 4.7 或 4.8 快照重现。

标签: c++ c++11 mingw


【解决方案1】:

这是数组头的一部分

#ifdef __EXCEPTIONS
  constexpr const_reference
  at(size_type __n) const
  {
return __n < _Nm ? 
       _M_instance[__n] : throw out_of_range(__N("array::at"));
  }
#else
  const_reference
  at(size_type __n) const
  {
if (__n >= _Nm)
  std::__throw_out_of_range(__N("array::at"));
return _M_instance[__n];
  }
#endif

主文件中的Undef __EXCEPTIONS(或将数组中的#ifdef 更改为#ifndef)会导致正确的输出。我不知道,这是否是正确的解决方案,但它有效。

UPD:我将数组标题中的代码更改为

#ifdef __EXCEPTIONS
  constexpr const_reference
  at(size_type __n) const
  {
return __n < _Nm ? 
       _M_instance[__n] : (throw out_of_range(__N("array::at"))),
                          _M_instance[__n];
   /*return __n < _Nm ? 
            _M_instance[__n] : throw out_of_range(__N("array::at"));*/
  }
#else
  const_reference
  at(size_type __n) const
  {
if (__n >= _Nm)
  std::__throw_out_of_range(__N("array::at"));
return _M_instance[__n];
  }
#endif

现在一切正常

【讨论】:

  • 未修补标头中的错误是 __n &lt; _Nm ? _M_instance[__n] : throw out_of_range(__N("array::at")) 临时评估为纯右值(因为如果操作数之一是 throw 表达式,则条件运算符必须评估为纯右值 - C++11 5.16/2)。这个临时的生命周期只到return 语句(C++11 12.2/5)的完整表达式结束。所以at() 函数返回一个不再有效的常量引用——结果是未定义的行为。
  • 一个可怕的错误 (return a? b:throw c;) --- 不会想到任何附带的 c++ 容器都会出现这种初学者错误。
  • @MichaelBurr:我认为它可能值得自己回答!
  • @Walter,你为什么认为这是初学者的错误? (return a? b:throw c;) 本身没有任何问题,如果函数没有通过引用返回就可以了。故意这样写是为了让array::at 成为constexpr(但正如迈克尔指出的那样,它不起作用。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-07
  • 2020-04-02
  • 1970-01-01
相关资源
最近更新 更多