MSV C не удалось скомпилировать при преобразовании базового типа в производный тип в выражении константы - PullRequest
1 голос
/ 01 марта 2020

Код:

#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>]

Я пишу класс массива самостоятельно. Я решил написать базовый класс итератора массива, чтобы любой класс итератора массива мог просто наследовать его, чтобы сохранить некоторые нажатия клавиш. Для этого я должен привести базовый класс к производному классу итератора, когда необходимо вернуть сам итератор, поэтому в перегрузке iterator_type &operator++() есть return *static_cast<iterator_type *>(this);.

Однако в контексте constexpr MSV C не удалось скомпилировать, когда итератор является полем в классе, но успешно скомпилировал, когда итератор является локальной переменной. В сообщении об ошибке говорится, что выражение не является константой, поскольку вызов функции включает приведение типа Dynami c (см. Выше).

G CC и Clang успешно скомпилированы в обоих случаях.

Интересно, что в Visual Studio значение y можно предварительно просмотреть (наведя на него курсор), как и любую другую переменную constexpr (что заставляет меня думать, что MSV C, вероятно, неверно).

Редактировать: Последняя предварительная версия MSV C по-прежнему не компилируется.

Редактировать: Я сообщил об этой ошибке в Microsoft здесь .

Вопросы :

  1. Какой компилятор является правильным в соответствии со стандартом?

  2. Есть ли лучший способ сделать то, что я делаю (т.е. написание базы класс для наследования классов итераторов)?

1 Ответ

0 голосов
/ 01 марта 2020

Я не вижу причин, по которым это не должно компилироваться. Сообщение об ошибке

MSV C не имеет смысла, потому что array_iterator<value_type> с value_type = int равно array_iterator<int> и iterator_type также array_iterator<int>. Таким образом, приведение является действительным.

this также ссылается на объект, который был создан в этой оценке константного выражения, которая началась с вызова error(), поэтому нет причины отклонять его как переоценку константы expression.

И G CC, и Clang, похоже, согласны с этим анализом, поэтому, скорее всего, это ошибка MSV C. Я также не вижу никаких проблем с CRTP, который вы используете здесь. Я не вижу преимущества, так как, поскольку существует только одно определение класса (array_iterator), унаследованное от базы CRTP.

Понижение рейтинга представляется целесообразным. Вам нужно только убедиться, что экземпляр типа array_iterator_base никогда не создается напрямую. Вы можете, например, сделать конструкторы из array_iterator_base protected для достижения этого.

...