Это провальный тест, который добавляет ноль к неопределенному поведению нулевого указателя, ошибку компилятора или что-то еще? - PullRequest
0 голосов
/ 15 октября 2018

Я написал облегченную оболочку string_view для проекта C ++ 14, а с MSVC 2017 он вызывает static_assert во время компиляции, но тот же код во время выполнения проходит обычную assert,У меня такой вопрос: это ошибка компилятора, манифест неопределенного поведения или что-то еще целиком?

Вот перегнанный код:

#include <cassert> // assert
#include <cstddef> // size_t

class String_View
{
    char const* m_data;
    std::size_t m_size;
public:
    constexpr String_View()
      : m_data( nullptr ),
        m_size( 0u )
    {}

    constexpr char const* begin() const noexcept
    { return m_data; }
    constexpr char const* end() const noexcept
    { return m_data + m_size; }
};

void static_foo()
{
    constexpr String_View sv;

//    static_assert( sv.begin() == sv.end() ); // this errors
    static_assert( sv.begin() == nullptr );
//    static_assert( sv.end() == nullptr ); // this errors
}

void dynamic_foo()
{
    String_View const sv;

    assert( sv.begin() == sv.end() ); // this compiles & is optimized away
    assert( sv.begin() == nullptr );
    assert( sv.end() == nullptr ); // this compiles & is optimized away
}

Вот ссылка Compiler Explorer что я использовал для репликации проблемы.

Из того, что я могу сказать, добавление или вычитание 0 из любого значения указателя всегда допустимо:

Обходной путь:

Если я изменю свой метод end на следующий, сбой static_assert s пройдет.

constexpr char const* end() const noexcept
{ return ( m_data == nullptr
           ? m_data
           : m_data + m_size ); }

Мастеринг:

Я подумал, что, возможно, само выражение m_data + m_size является UB, до того как m_size == 0 будет оценено.Тем не менее, если я заменю реализацию end на бессмысленную return m_data + 0;, это все равно вызовет две static_assert ошибки.: - /

Обновление:

Похоже, что это ошибка компилятора, которая была исправлена ​​между 15.7 и 15.8.

Ответы [ 2 ]

0 голосов
/ 15 октября 2018

Это похоже на ошибку MSVC, проект стандарта C ++ 14 явно разрешает добавление и вычитание значения 0 к указателю для сравнения, равного себе, из [expr.add] p7 :

Если значение 0 добавляется или вычитается из значения указателя, результат сравнивается равным исходному значению указателя.Если два указателя указывают на один и тот же объект или оба указывают один за концом одного и того же массива или оба равны нулю, и оба указателя вычитаются, результат сравнивается равным значению 0, преобразованному в тип std :: ptrdiff_t.

Похоже, дефект CWG 1776 приводит к p0137, который скорректировал [expr.add] p7 , чтобы явно сказать null pointer.

последний черновик сделал это еще более явным [expr.add] p4 :

Когда выражение J, имеющее целочисленный тип, добавляется или вычитается из выражения P типа указателя,результат имеет тип P.
- Если P оценивается как нулевое значение указателя, а J - как 0, результатом является нулевое значение указателя.
- В противном случае, если P указывает на элемент x [i] изобъект массива x с n элементами 85, выражения P + J и J + P (где J имеет значение j) указывают на (возможно, гипотетический) элемент x [i + j], если 0≤i + j≤n, ивыражение P - J указывает на (возможно, гипотетический) элемент x [i−j] если 0≤i − j≤n.(4.3).
- В противном случае поведение не определено.

Это изменение было внесено в редакционную статью. См. этот выпуск github и этот PR .

MSVC здесь несовместим в том, что он позволяет добавлять и вычитать ноль в константном выражении, как это делают gcc и clang.Это является ключевым, потому что неопределенное поведение в константном выражении плохо сформировано и поэтому требует диагностики.Учитывая следующее:

constexpr int *p = nullptr  ;
constexpr int z = 0 ;
constexpr int *q1 = p + z;
constexpr int *q2 = p - z;

gcc, clang и MSVC допускают постоянное выражение ( пример с живым крестиком ), хотя, к сожалению, MSVC вдвойне несовместим в том, что допускает также ненулевое значениес учетом следующего:

constexpr int *p = nullptr  ;
constexpr int z = 1 ;
constexpr int *q1 = p + z;
constexpr int *q2 = p - z;

и clang, и gcc говорят, что он плохо сформирован, в то время как MSVC этого не делает ( live godbolt ).

0 голосов
/ 15 октября 2018

Я думаю, что это определенно ошибка в том, что MSVC оценивает константные выражения, поскольку у GCC и Clang нет проблем с кодом, и стандарт ясен, что добавление 0 к нулевому указателю приводит к нулевому указателю ([expr.добавить] / 7).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...