constexpr函数存在可能的错误?

3
以下代码是否正确使用了constexpr函数?它尝试以各种方式访问static constexpr成员变量_size
使用g++编译没有问题,但在msvc-2017 / 2019clang中失败。
可通过godbolt here进行测试的代码示例。
似乎如果用等效的宏解决方法,就可以使其(在任何地方)都能编译。取消注释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

根据cppreference.com:

引用变量可以声明为constexpr(它们的初始化器必须是引用常量表达式):

这个程序的问题在于你试图在constexpr上下文中使用运行时变量的引用(在main函数开头声明)。虽然对象内部访问的变量是全局状态(静态),并且在constexpr上下文中,但这些运行时变量的地址在这种情况下不能使用。我无法使用除GCC 9.0及更早版本之外的任何现代编译器编译您提供的代码。GCC trunk无法编译。

我能够使用我们可用的最佳工具之一,系统级程序员Matt Godbolt的Compiler Explorer进行测试。简化示例

有趣的是,如果我们决定通过值复制它们来调用goo(),则似乎可以使用这些变量。通过从函数签名中删除“&”符号,我能够在多个clang、gcc和msvc版本上编译此程序:

#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;
}

编辑:

由于我无法确认是否是编译器的 bug(因为我无法重现您所说的结果),所以希望我能帮助您使用您提供的代码,因为我认为这是您遇到的合法用例。


谢谢您查看此内容,但我不确定这是否解决了问题 --- 在编译时访问的_size成员变量在所有地方都是static constexpr,因此我不清楚为什么这不起作用。使用g++ 7.4和通过ideone(g++ 8.3)编译也可以为我编译。 - Darren Engwirda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接