MSVC在常量表达式中将基本类型转换为派生类型时无法编译

问题描述 投票:1回答:1

代码:

#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

问题:

  1. 哪个编译器根据标准是正确的?

  2. 是否有更好的方法来做我正在做的事情(即为继承的迭代器类编写基类?

c++ visual-c++ visual-studio-2019 constexpr
1个回答
0
投票

我看不出不应编译的任何理由。

MSVC的错误消息没有意义,因为带有array_iterator<value_type>value_type = intarray_iterator<int>,而iterator_type也是array_iterator<int>。因此强制转换有效。

[this还引用在此常量表达式求值中创建的对象,该对象始于对error()的调用,因此没有理由将其作为常量表达式的子求值而拒绝。

GCC和Clang似乎都同意这种分析,因此很可能是MSVC错误。我在这里使用的CRTP也没有任何问题。我没有看到好处,因为因为它是从CRTP基础继承的仅一个类定义(array_iterator)。

向下转换似乎很合适。您只需要确保永远不会直接创建类型为array_iterator_base的实例。您可以例如使array_iterator_base protected的构造函数实现这一点。

© www.soinside.com 2019 - 2024. All rights reserved.