Возможная ошибка с функциями constexpr? - PullRequest
3 голосов
/ 03 марта 2020

Является ли следующий код правильным использованием функций constexpr? По сути, он пытается получить доступ к переменной static constexpr _size различными способами.

Компилируется без проблем, используя g++, но завершается неудачно с msvc-2017 / 2019 и clang.

. Пример кода, доступный для тестирования через godbolt здесь .


Кажется, что это можно сделать для компиляции (везде), если функция constexpr заменена эквивалентным макрообработкой , Раскомментируйте USE_MACRO_WORKAROUND для проверки.

Мне кажется, это наводит на мысль об ошибке компилятора, связанной с constexpr функциями?

(Поскольку макро-версия работает, это предполагает, что у компилятора должно быть достаточно информации времени компиляции, чтобы иметь дело с функцией constexpr. Очевидно, g++ может сделать это ...)


(Этот пример - просто выдумка. Настоящий код является частью этой библиотеки ).

#include <cstddef>

//define USE_MACRO_WORKAROUND

template <size_t N = +1>
struct expansion 
{
    size_t static constexpr _size = N ;
    double                  _xdat [ N ] ;
};

#if defined(USE_MACRO_WORKAROUND)

// ugly macro-based hack that's equiv. to compile 
// time foo()...
// does compile everywhere

#define foo(_aa, _bb) _aa._size + _bb._size

#else

// why does this cause problems? works for g++ 7,
// 8, 9, but not msvc, etc

template <size_t NA, size_t NB>
inline size_t constexpr foo (
    expansion <NA> const& _aa,
    expansion <NB> const& _bb
    )
{
    return _aa._size + _bb._size;
}

#endif  //USE_MACRO_WORKAROUND

template <size_t NA, size_t NB>
inline void goo (
    expansion <NA> const& _xx,
    expansion <NB> const& _yy
    )
{   // this will not compile with msvc, reporting
    // C2131: expression did not evaluate to a constant
    expansion<foo(_xx, _yy)> _tt;
}

int main ()
{
    expansion< 2 > _x2;
    expansion< 4 > _x4;

    // this seems to work for both g++ and msvc
    expansion<foo(_x2, _x4)> _x6;

    // via msvc, this leads to the errors above
    goo (_x2, _x4) ;

    return 0;
}

1 Ответ

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

Согласно cppreference.com:

Ссылочные переменные могут быть объявлены constexpr (их инициализаторы должны быть выражениями ссылочных констант):

Проблема с этой программой что вы пытаетесь использовать ссылку на переменные времени выполнения (объявленные в начале main) в контексте constexpr. Хотя переменная, доступ к которой осуществляется внутри объектов, находится в глобальном состоянии (stati c) и в контексте constexpr, адрес этих переменных времени выполнения не может использоваться в этом случае. Мне не удалось получить предоставленный вами код, компилируемый любым современным компилятором, кроме G CC 9.0 и более ранних. G CC trunk не удалось скомпилировать.

Я смог проверить это с помощью одного из лучших инструментов, доступных для нас, программистов системного уровня, Проводник компилятора Мэтта Годболта . Упрощенный пример

Эти переменные, как ни странно, можно использовать, если вместо этого мы решим скопировать их по значению при вызове goo(). Мне удалось заставить эту программу компилироваться на нескольких версиях каждого клана, g cc и msv c, удалив амперсанды из сигнатуры функции:

#include <cstddef>
#include <iostream>

template <
    size_t N = +1
         >
class expansion
{
public:
    size_t static constexpr _size = N ;

    double                  _xdat [ N ] ;
    size_t                  _xlen = 0 ;
};

template <
    size_t NA, size_t NB
         >
inline size_t constexpr foo (
    expansion <NA> const& _aa,
    expansion <NB> const& _bb
    )
{
    return _aa._size + _bb._size;
}

template <
    size_t NA, size_t NB
         >
inline void goo (
    expansion <NA> const _xx,
    expansion <NB> const _yy
    )
{
    // this will not compile with msvc, reporting
    // C2131: expression did not evaluate to a constant
    size_t
    constexpr _nn = foo(_xx, _yy);

    expansion<_nn> _tt;
}

int main ()
{
    expansion< 2 > _x2;
    expansion< 4 > _x4;

    // this seems to work for both g++ and msvc
    size_t
    constexpr _n6 = foo(_x2, _x4);

    expansion<_n6> _x6;

    // via msvc, this leads to the errors above
    goo (_x2, _x4) ;

    std::cout << _n6 << std::endl;

    return 0;
}

РЕДАКТИРОВАТЬ:

Поскольку я не смог подтвердить, является ли это ошибкой компилятора (поскольку я не смог воспроизвести результаты, которые вы сказали, что вы получили), я надеюсь, что смог помочь вам использовать предоставленный вами код, так как я предполагаю, что это законный вариант использования, с которым вы столкнулись.

...