我使用以下代码在 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,
^
随着 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 就足够了。