代码:
#include <cstddef>
template <typename value_type, typename iterator_type>
class array_iterator_base
{
protected:
value_type *ptr;
public:
constexpr array_iterator_base() : ptr(nullptr) {}
constexpr iterator_type &operator++()
{
++ptr;
return *static_cast<iterator_type *>(this); // [1]
}
};
template <typename value_type>
class array_iterator : public array_iterator_base<value_type, array_iterator<value_type>>
{
public:
constexpr array_iterator(value_type *ptr)
{
this->ptr = ptr;
}
};
template <typename value_type, std::size_t Size>
class array
{
public:
using iterator = array_iterator<value_type>;
value_type m_data[Size];
constexpr iterator begin() { return iterator(m_data); }
};
class Demo
{
using storage = array<int, 3>;
using iterator = typename storage::iterator;
private:
storage m_arr = { 1, 2, 3 };
iterator m_iter = m_arr.begin();
public:
constexpr Demo() {}
constexpr void field()
{
++m_iter; // MSVC: failed
}
constexpr void local_variable()
{
storage arr = { 1,2,3 };
iterator iter = arr.begin();
++iter; // MSVC: OK
}
};
constexpr int ok()
{
Demo demo;
demo.local_variable();
return 1;
}
constexpr int error()
{
Demo demo;
demo.field();
return 1;
}
int main()
{
constexpr int x = ok();
// GCC: OK
// Clang: OK
// MSVC: OK
constexpr int y = error(); // [2]
// GCC: OK
// Clang: OK
// MSVC: error
}
由于行[2]
而在行[1]
处出错:
Expression did not evaluate to a constant.
Failure was caused by cast of object of dynamic type
array_iterator<value_type>
to type
iterator_type
with
[value_type=int]
[iterator_type=array_iterator<int>]
我正在编写一个数组类。我决定编写一个数组迭代器基类,以便任何数组迭代器类都可以从其继承以保存一些击键。为此,当需要返回迭代器本身时,我必须将基类转换为派生的迭代器类,以便在return *static_cast<iterator_type *>(this);
重载中存在iterator_type &operator++()
。
但是,在constexpr
上下文中,当迭代器为类中的字段时,MSVC编译失败,但当迭代器为局部变量时,则成功编译。错误消息指出表达式不是常量,因为函数调用涉及强制转换动态类型(请参见上文)。
在两种情况下,GCC和Clang均已成功编译。
有趣的是,在Visual Studio中,y
的值实际上可以像其他任何constexpr
变量一样预览(通过将光标悬停在其上)(这使我认为MSVC可能是错误的。)
编辑:MSVC的最新预览版仍然无法编译。
编辑:我已将此错误报告给Microsoft here。
问题:
哪个编译器根据标准是正确的?
是否有更好的方法来做我正在做的事情(即为继承的迭代器类编写基类?
我看不出不应编译的任何理由。
MSVC的错误消息没有意义,因为带有array_iterator<value_type>
的value_type = int
是array_iterator<int>
,而iterator_type
也是array_iterator<int>
。因此强制转换有效。
[this
还引用在此常量表达式求值中创建的对象,该对象始于对error()
的调用,因此没有理由将其作为常量表达式的子求值而拒绝。
GCC和Clang似乎都同意这种分析,因此很可能是MSVC错误。我在这里使用的CRTP也没有任何问题。我没有看到好处,因为因为它是从CRTP基础继承的仅一个类定义(array_iterator
)。
向下转换似乎很合适。您只需要确保永远不会直接创建类型为array_iterator_base
的实例。您可以例如使array_iterator_base
protected
的构造函数实现这一点。