带有 consteval 关键字的 Lambda 在 LLVM 工作时会导致 GCC 和 MSVC 出错。谁是对的?

问题描述 投票:0回答:1

我使用以下代码在 MSVC 19.33 和 GCC 12.2 中遇到错误。

LLVM 16(

clang++
libc++
)编译它。 godbolt LLVM

#include <string>
#include <string_view>

int main() {
    static constexpr auto enlarge =
        [](std::string&& target, std::size_t const new_size) consteval {
            auto const old_size = target.size();
            if(new_size < old_size) {
                throw "new size is smaller then old size";
            }
            target.resize(new_size);
            target[old_size] = 0;
            return target.c_str();
        };

    using namespace std::literals;
    static_assert(enlarge("test"s, 32) == "test"sv);
}

GCC 12.2 (

g++
&
libstdc++
) 抱怨:godbolt GCC

In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/string:41,
                 from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/allocator.h: In function 'int main()':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/allocator.h:185:52: error: 'enlarge.main()::<lambda(std::string&&, std::size_t)>(std::literals::string_literals::operator""s(const char*, std::size_t)(4), 32)' is not a constant expression because it refers to a result of 'operator new'
  185 |             return static_cast<_Tp*>(::operator new(__n));
      |                                      ~~~~~~~~~~~~~~^~~~~

MSVC 19.33 抱怨:MS godbolt MSVC

<source>(17): error C7595: 'main::<lambda_1>::operator ()': call to immediate function is not a constant expression
<source>(17): note: (sub-)object points to memory which was deallocated during constant evaluation
<source>(17): error C2446: '==': no conversion from 'std::string_view' to 'int'
<source>(17): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

如果我从 lambda 中删除关键字

consteval
,它适用于所有三个具有相应标准库的编译器。 (LLVM, GCC, MSVC)

这是 GCC 和 MSVC 中的错误,还是

consteval
对执行的影响不同于
constexpr
上下文中的调用,而 LLVM 接受它是错误的?


我认为这是一个单独的问题,但为了完整性:当我使用

clang++
12.2 运行
libstdc++
16 时,无论
consteval
错误如何: godbolt clang++ with libstdc++

<source>:17:19: error: static assertion expression is not an integral constant expression
    static_assert(enlarge("test"s, 32) == "test"sv);
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/basic_string.h:620:2: note: undefined function '_M_construct<const char *>' cannot be used in a constant expression
        _M_construct(__s, __s + __n, std::forward_iterator_tag());
        ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/basic_string.h:4330:14: note: in call to 'basic_string(&"test"[0], 4, std::allocator<char>())'
    { return basic_string<char>{__str, __len}; }
             ^
<source>:17:27: note: in call to 'operator""s(&"test"[0], 4)'
    static_assert(enlarge("test"s, 32) == "test"sv);
                          ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/basic_string.h:330:9: note: declared here
        _M_construct(_FwdIterator __beg, _FwdIterator __end,
        ^
c++ visual-c++ g++ c++20 clang++
1个回答
0
投票

随着 P2564R3 对当前 C++23 草案所做的更改,代码格式正确,因为

enlarge("test"s, 32)
不是 立即调用 因为它是 明显常量计算表达式 的子表达式(
static_assert
的操作数)。

然而,如果没有那个改变,MSVC 和 GCC 拒绝代码是正确的,而 Clang 接受它是错误的。如果没有变化,

enlarge("test"s, 32)
是立即调用,立即调用形成完整表达式。这意味着在评估期间创建的临时对象将在评估的最后一步被销毁。

因此,

target
所指的临时对象在评估
enlarge("test"s, 32)
的最后一步被销毁。但是,您将使用
return target.c_str();
返回指向该对象的指针。立即调用必须是常量表达式,但无效指针不是常量表达式的允许结果。

这就是 MSVC 和 GCC 所抱怨的。这两条错误消息都特别抱怨返回指向已释放内存的指针,但如果不涉及动态内存分配,则同样应该成立,例如因为 SSO 就足够了。

© www.soinside.com 2019 - 2024. All rights reserved.