为什么会有“从不使用非文字类型”?规则在constexpr函数? [英] why is there a "never use non-literal type" rule in constexpr functions?

查看:251
本文介绍了为什么会有“从不使用非文字类型”?规则在constexpr函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

采用以下法律代码:

bool bar();

template <class T>
constexpr bool foo(T t) {
  if (t>0) {
    return true;
  }
  return bar();
}


int main() {
  //constexpr bool cb1 = foo(-1); // error as expected  because it would attempt to call bar()
  constexpr bool cb2 = foo(1); // ok
}

https://godbolt.org/z/UWt_3A

因此,只要我们在Windows中不打非constexpr代码路径我们的constexpr的编译时评估上下文结构良好。整洁!

So, as long as we don't hit a non-constexpr code-path in a compile time evaluation context our constexpr is well formed. Neat!

但是,如果我应用相同的实际概念,但碰巧在条件代码路径中包含非文字类型,例如 std ::字符串,然后标准说不行:

However, if I apply the same practical notion, but happen to include a non-literal type in a conditional code-path, such as std::string, then the standard says no-no:

#include <string>

bool bar(std::string);

template <class T>
constexpr bool foo(T t) {
  if (t>0) {
    return true;
  }
  std::string s = "abc";
  return bar(s);
}


int main() {
  //constexpr bool cb1 = foo(-1); // error as expected
  constexpr bool cb2 = foo(1); // this is also an error now :(
}

https://godbolt.org/z/iHThCq

这背后的原理是什么?为什么使用std :: string即使不真正被构造(或销毁)也不惜一切代价吗?

What is the rationale behind this? Why is using std::string illegal at all cost even though it never actually gets constructed (or destroyed)?

奖金问题:为什么下面的命令合法: https://godbolt.org/z/L3np-u (上面略有变化,但未定义std :: string)?!

Bonus question: why is the following legal then: https://godbolt.org/z/L3np-u (slight variation on above, without defining std::string) ?!

推荐答案

我只是在这里猜测,但这可能是因为 std :: string s = abc ; 一个自动变量并在函数开始时分配在栈中(即使尚未构造)也会破坏 constexpr 规则吗?

I'm just guessing here, but can it be because being std::string s = "abc" an automatic variable and allocated in the stack at function begin (even if not yet constructed) breaks the constexpr rules?

如果我将代码更改为:

using namespace std::string_literals;

bool bar(std::string);

template <class T>
constexpr bool foo(T t) {
    if (t>0) {
        return true;
    }
    else {
        //std::string ss = "abc"s;
        return bar("abc"s);
    }
    return false;
}

因为无需分配其编译内容。

as there is no need to allocate anything it compiles.

我在这里解释我的推理(以及对评论的回应),因为我需要的空间比评论中的空间还多。

I explain my reasoning (and response to comments) here, as I need more space than in a comment.

正如@ StoryTeller-UnslanderMonica所说,猜测是错误的依据

As @StoryTeller-UnslanderMonica says, "guessing is a bad basis for answering questions".

当然可以。这就是为什么我开始这么说:我在猜。那是有原因的。

Absolutely yes. That's why I begin saying just that: I'm guessing. And that have a reason.

我不喜欢正常猜测,但我发现这很有趣,想想一想,看看是否有人说我错了(我是

I don't like to guess normally but I found this interesting and want to throw a thought to see if someone says I'm wrong (something I'm pretty ready to accept.)

但是要指出的是,文字类型变量通常存储在某些只读内存数据段中(除非它们是数字,否则可以直接转换为ASM MOV / ...说明),而不是堆栈中。

But going to the point, literal type variables are normally stored at some read only memory data segment (unless they are numbers, those can be translated directly to ASM MOV/... instructions), not at stack.

如果声明为自动(在堆栈中存储):

If declared automatic (storing at stack):


存储持续时间

Storage duration

程序中的所有对象都具有以下存储持续时间之一:

All objects in a program have one of the following storage durations:

自动存储持续时间。对象的存储空间分配在封闭代码块的开头,并在末尾释放。除了声明为static,extern或thread_local的那些对象外,所有本地对象都具有此存储期限。

automatic storage duration. The storage for the object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local.

(强调)。

因此,即使在 if 之后声明,也会分配存储空间,并且在任何情况下都应释放存储空间(在OP所示的示例中)。

So, even if declared after the if, the storage is allocated and should be deallocated in any case (in the example shown by OP.)

实际上,如果这样做的话:

In fact, if done like this:

template <class T>
constexpr bool foo(T t) {
    if (t>0) {
        return true;
    }
    const std::string ss = "abc"s;
    return bar(ss);
}

错误是:

main.cc:15:16: error: call to non-‘constexpr’ function ‘std::__cxx11::basic_string<char> std::literals::string_literals::operator""s(const char*, std::size_t)’

为什么? 我猜是因为,自动将对象的存储空间分配在封闭代码块的开头 (从函数开始)无论执行代码路径如何。

why? I guess because, being automatic, "the storage for the object is allocated at the beginning of the enclosing code block" (beginning of the function) no matter the execution code path.

此外,如果将其声明为 constexpr ,则会引入析构函数:

Moreover, if you declare it constexpr, it introduces the destructor:

template <class T>
constexpr bool foo(T t) {
    if (t>0) {
        return true;
    }
    constexpr std::string ss = "abc"s;
    return bar(ss);
}

错误:

main.cc:19:32: error: temporary of non-literal type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} in a constant expression
     constexpr std::string ss = "abc"s;
                                ^~~~~~
In file included from /usr/include/c++/8/string:52,
                 from main.cc:2:
/usr/include/c++/8/bits/basic_string.h:77:11: note: ‘std::__cxx11::basic_string<char>’ is not literal because:
     class basic_string
           ^~~~~~~~~~~~
/usr/include/c++/8/bits/basic_string.h:77:11: note:   ‘std::__cxx11::basic_string<char>’ has a non-trivial destructor
main.cc: In instantiation of ‘constexpr bool foo(T) [with T = int]’:
main.cc:25:29:   required from here
main.cc:19:27: error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘ss’ is not literal
     constexpr std::string ss = "abc"s;

我认为关键是:'std :: __ cxx11 :: basic_string< char>'具有一个非平凡的析构函数

因此,在执行代码路径之前应考虑对析构函数的理论调用。

so the theoretical call to the destructor is taken in account before the execution code path.

为什么?

因为对象的存储空间分配在了封闭代码块的开头

以下内容:

template <class T>
constexpr bool foo(T t) {
    if (t>0) {
        return true;
    }
    return bar("abc"s);
}

创建一个临时文件:

main.cc:19:15: error: call to non-‘constexpr’ function ‘bool bar(std::__cxx11::string)’
     return bar("abc"s);

但是

template <class T>
constexpr bool foo(T t) {
    if (t>0) {
        return true;
    } else {
        return bar("abc"s);
    }
    return false;
}

如果执行路径转到<$,则创建临时 only c $ c> else (不是这种情况。)

creates the temporary only if the execution path goes to the else (which is not the case.)

如前所述,这是一个猜测,但我认为基于 的猜测,而不仅仅是盲目尝试。

As said, this is a guess, but I think a based guess, not just a blind try.

同样,我相信这取决于编译器的实现。我绝不是C ++标准专家,但我在任何文档中都找不到这种明显的情况。

Again, I'm convinced this depends on compiler implementation. I'm by no means a C++ standard expert, but I haven't been able to find this explicit case in any document.

我已经在 gdb 看看是否输入 foo 函数:

I've run the program in gdb to see if it enters in the foo function:

bool bar(std::string);

template <class T>
constexpr bool foo(T t) {
    if (t>0) {
        return true;
    } else {
        //std::string ss = "abc"s;
        return bar("abc"s);
    }
    return false;
}

int main() {
    //constexpr bool cb1 = foo(-1); // error as expected
    constexpr bool cb2 = foo(1); // this is also an error now :(

    cout << "Bool: " << cb2 << endl;
    
    return 0;
}

它的链接没有定义 bar 所以...

it links without bar being defined so...

manuel@desktop:~/projects$ g++ -Wall -Wextra -g main.cc -o main --std=gnu++2a -Wpedantic && time ./main
Bool: 1

real    0m0,002s
user    0m0,000s
sys 0m0,002s
manuel@desktop:~/projects$ gdb ./main
GNU gdb (Debian 8.2.1-2+b3) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./main...done.
(gdb) b main
Breakpoint 1 at 0x117d: file main.cc, line 27.
(gdb) r
Starting program: /home/manuel/projects/main 

Breakpoint 1, main () at main.cc:27
27      constexpr bool cb2 = foo(1); // this is also an error now :(
(gdb) s
29      cout << "Bool: " << cb2 << endl;
(gdb) s
Bool: 1
31      return 0;
(gdb) s
32  }
(gdb) q
A debugging session is active.

    Inferior 1 [process 18799] will be killed.

Quit anyway? (y or n) y

这篇关于为什么会有“从不使用非文字类型”?规则在constexpr函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆