我在MSVC 19.33和GCC 12.2中得到了以下代码的错误。
LLVM 16(clang++
with 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中的一个bug,还是consteval
对执行有另一种影响,而不是constexpr
上下文中的调用,LLVM接受这一点是错误的?
我假设这是一个单独的问题,但为了完整性:当我运行clang++
16和libstdc++
12.2时,无论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,
^
1条答案
按热度按时间2ul0zpep1#
通过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就足够了,那么这也应该成立。