Функция constexpr MSVC 'xyz' не может привести к постоянному выражению - PullRequest
0 голосов
/ 21 декабря 2018

Я сделал функцию, которая объединяет несколько меньших значений в одно большее значение, сохраняя при этом двоичное представление значений (например, для построения int argb из множества unsigned char r, g, b, a).Я знаю, что я также могу добиться этого путем сдвига битов значений, но это не вопрос этого вопроса.

Однако, если я использую функцию для фактического генерирования целого числа из этих значений, msvc выдает ошибку компилятора:

error C3615: constexpr function 'Color::operator int' cannot result in a constant expression
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of '<lambda_dcb9c20fcc2050e56c066522a838749d>::operator ()'

Здесь - полный образец.Clang и gcc компилируют код, но msvc отказывается:

#include <type_traits>
#include <memory>

namespace detail
{
    template <typename From, typename To, size_t Size>
    union binary_fusion_helper
    {
        const From from[Size];
        const To to;
    };

    template <typename To, typename Arg, typename ...Args, typename = std::enable_if_t<(... && std::is_same_v<std::remove_reference_t<Arg>, std::remove_reference_t<Args>>)>>
    constexpr To binary_fusion(Arg arg, Args... args)
    {
        using in_t = std::remove_reference_t<Arg>;
        using out_t = To;
        static_assert(sizeof(out_t) == sizeof(in_t) * (sizeof...(Args) + 1), "The target type must be of exact same size as the sum of all argument types.");
        constexpr size_t num = sizeof(out_t) / sizeof(in_t);
        return binary_fusion_helper<in_t, out_t, num> { std::forward<Arg>(arg), std::forward<Args>(args)... }.to;
    }
}

template <typename To>
constexpr auto binary_fusion = [](auto ...values) -> To
{
    return detail::binary_fusion<std::remove_reference_t<To>>(values...);
};

struct Color
{
    float r, g, b, a;

    explicit constexpr operator int() const noexcept
    {
        return binary_fusion<int>(static_cast<unsigned char>(r * 255), static_cast<unsigned char>(g * 255),
                                  static_cast<unsigned char>(b * 255), static_cast<unsigned char>(a * 255));
    }
};

Clang и gcc просто игнорируют, что код никогда не будет работать как constexpr или msvc неверен?И если msvc верен, почему функция не может быть запущена во время компиляции?

1 Ответ

0 голосов
/ 21 декабря 2018

Каждый компилятор корректен.Правило в [dcl.constexpr] / 5 :

Для функции constexpr или конструктора constexpr, который не является ни значением по умолчанию, ни шаблоном, если не существует значений аргумента, таких каквызов функции или конструктора может быть оцененным подвыражением основного константного выражения, или, для конструктора, инициализатором константы для некоторого объекта ([basic.start.static]), программа плохо сформирована, диагностика не требуется.

Не существует набора аргументов, которые можно передать binary_fusion, которые позволили бы его оценивать как выражение основной константы, поэтому объявление его constexpr неверно сформировано, NDR.Причина этого в том, что detail::binary_fusion() инициализирует объединение с одним активным членом, а затем читает из неактивного члена, что вам запрещено делать в константных выражениях ( [expr.const] /4.8):

преобразование lvalue в rvalue, которое применяется к glvalue, который относится к неактивному члену объединения или его подобъекту;

MSVCкак-то диагностирует это, gcc / clang случается не с.Все компиляторы правильно диагностируют это:

constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...