下面的程序
template<class T>
consteval auto foo(const T&) {
return 0;
}
template<class T>
consteval auto bar(const T& t) {
auto n = foo(t);
return n;
}
int main() {
static_assert(foo("abc") == 0);
static_assert(bar("abc") == 0);
}
Run Code Online (Sandbox Code Playgroud)
在 GCC 中构建得很好,但 Clang 拒绝了它,并显示以下消息:
error: call to consteval function 'foo<char[4]>' is not a constant expression
note: in instantiation of function template specialization 'bar<char[4]>' requested here
static_assert(bar("abc") == 0);
note: function parameter 't' with unknown value cannot be used in a constant expression
auto …Run Code Online (Sandbox Code Playgroud) 我正在测试这段代码(https://godbolt.org/z/fe6hhbeqW)...
// Returns the nth type in a parameter pack of types (ommited for clarity)
// template <std::size_t N, typename...Ts>
// nth_type{}
template <typename... Ts>
struct Typelist{
template <typename T>
consteval static std::size_t pos() noexcept {
for(std::size_t i{}; i < sizeof...(Ts); ++i) {
using TN = nth_type_t<i, Ts...>;
if (std::is_same_v<T, TN>)
return i;
}
return sizeof...(Ts);
}
};
Run Code Online (Sandbox Code Playgroud)
我很困惑它不起作用。GCC 和 clang 同意i不作为常量表达式,因此他们拒绝让我将其作为模板参数传递。然而,i在编译时是清楚的,因此,根据我有限的理解,编译器使用它来实例化模板应该没有任何问题。
这有什么理由不起作用吗?将来有用吗?我已经用两个编译器的主干版本进行了测试,结果相同。
在下一个程序中,struct B有立即consteval默认构造函数,它不会初始化i字段。然后这个构造函数被用来创建一个临时的并且它的i字段保持不变:
struct B {
bool b = true;
int i;
consteval B() {}
};
static_assert( B{}.b );
Run Code Online (Sandbox Code Playgroud)
Clang 和 MSVC 对此很满意。但海湾合作委员会抱怨道:
error: 'B{true}' is not a constant expression
7 | static_assert( B{}.b );
| ^
error: 'B()' is not a constant expression because it refers to an incompletely initialized variable
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/x4n6ezrhT
这里是哪个编译器?
更新:
我向 GCC 报告了这个问题:https://gcc.gnu.org/bugzilla/show_bug.cgi? id=104512 并通过解释关闭
这暗示 MSVC 和 clang 确实都不正确。EDG 还正确实现了 static_assert,而不是直接函数上下文。
在(重新)实现一个简单的 constexpr 映射时,我写了这个(godbolt):
template <class key_type, class value_type, int N>
class flat_map
{
private:
struct pair
{
key_type key;
value_type value;
};
const pair elements[N];
public:
consteval flat_map(const pair (&arr)[N]) noexcept
: elements(arr) // works on gcc?!
{}
[[nodiscard]] consteval value_type operator[](const key_type key) const
{
for (const pair &elem : elements)
if (elem.key == key)
return elem.value;
throw "Key not found";
}
};
constexpr flat_map<int, char, 3> m = {{
{ 4, 'a' }, { -1, 'b' …Run Code Online (Sandbox Code Playgroud) 这可能是一个愚蠢的问题,但我感到困惑。我有一种感觉,立即(consteval)函数具有在编译时被执行,我们根本无法看到它的身体在二进制。
这篇文章显然支持我的感觉:
这意味着[immediate]函数仅在编译时可见。该函数不会发出符号,您不能使用该函数的地址,并且调试器之类的工具将无法显示它们。在这种情况下,立即函数类似于宏。
在Herb Sutter的出版物中也可以找到类似的强烈主张:
请注意,C ++ 20草案已经包含了第一轮与反射相关的工作,并纳入了标准:确保函数在编译时运行的 consteval函数,这些函数来自于反射工作,并且专门用于处理反射信息。
但是,有很多证据对此事实不太清楚。
来自cppreference:
consteval-指定一个函数为立即函数,也就是说,对该函数的每次调用都必须产生一个编译时常量。
这并不意味着它已经在只有编译时被调用。
现在,人们普遍同意,将来对反射的语言支持应该使用constexpr函数,但是由于“反射函数”通常必须在编译时进行评估,因此实际上它们很可能是立即函数。
似乎这意味着我的想法,但仍未明确说出。根据同一建议:
但是,有时我们想表达的是,函数在被调用时(直接或间接)应始终产生常数,而非常数结果应产生错误。
同样,这并不意味着仅在编译时就必须评估函数。
从这个答案:
您的代码必须产生一个编译时常数表达式。但是编译时常量表达式在您使用它的上下文中不是可观察的属性,并且在链接甚至运行时执行它都没有副作用!在没有任何阻止的情况下
最后,有一个实时演示,其中consteval在运行时明确调用了函数。但是,我希望这是由于constevalclang尚未正确支持该事实,并且该行为实际上是不正确的,就像为什么consteval函数允许未定义的行为?
更准确地说,我想听听被引用文章的以下哪些陈述是正确的:
我正在处理一些constexpr使用函数的代码,我目前consteval尽可能将其重构为。
inline constexpr auto example() noexcept { /*...*/ }
Run Code Online (Sandbox Code Playgroud)
据我了解,inline上面的关键字在constexpr函数中已经是多余的了。
据我所知,noexcept关键字对于consteval函数来说是多余的,因为据我所知,它consteval必须在编译时进行评估,因此意味着 noexcept。这是真的还是我目前不考虑的东西(比如 constexpr exceptions)?
consteval auto example() { /*...*/ }
Run Code Online (Sandbox Code Playgroud) 在下面的代码中,如果here()停止consteval(完整 RT 或constexpr),则line()是f()inside的调用行main()。但有了consteval它的定义f()。这种差异从何而来?
#include <experimental/source_location>
#include <iostream>
consteval std::experimental::source_location here(
std::experimental::source_location loc = std::experimental::source_location::current())
{
return loc;
}
void f(const std::experimental::source_location& a = here())
{
std::cout << a.line() << std::endl; // will either print 17, or 10
}
int main()
{
f();
}
Run Code Online (Sandbox Code Playgroud)
考虑以下代码。我可以用 GCC 10.2.0 和 Clang 11.0.0 编译它(正如预期的那样):
#include <iostream>
template<int>
struct T {
static constexpr auto fun() noexcept { return 0; }
using type = std::remove_cvref_t<decltype(fun())>;
};
int main() {
decltype(T<1>::fun()) a = 1;
std::cout << a;
}
Run Code Online (Sandbox Code Playgroud)
如果我替换constexpr为consteval,那么 Clang 会抱怨std::remove_cvref_t<decltype(fun())>:
错误:无法在立即调用之外获取 consteval 函数“fun”的地址
GCC 编译它就好了。为什么?
我有一个 lambda 忽略其int参数并始终返回一个常量。如果我标记它consteval,编译就会失败,因为。编译器抱怨使用consteval非常量参数调用 lambda。但是参数与 lambda 有什么关系呢?
来自编译器资源管理器:
来源:3:16:错误:“i”的值在常量表达式中不可用 5 | 拉姆达(i);
void bar (auto lambda, int start, int end) {
for (int i=start; i<end; ++i) {
lambda(i);
}
}
int main( )
{
auto foo = [] (int) consteval { return 2;};
bar(foo, 1, 9);
return 0;
}
Run Code Online (Sandbox Code Playgroud) 在该准则下constexpr everything,随着constevalC++20的引入,越来越多的代码在编译时被评估。
这导致了一个显而易见的问题:我们如何调试它?
目前唯一的提示是编译器错误。但是如果代码编译了,但仍然没有达到预期的效果怎么办。有什么工具可以帮助解决这个问题吗?有没有检查的可能?
一个相关的问题是:如何知道哪些会在编译时真正被“执行”,哪些会在有限定符的情况下保持运行。