使用stoi声明的常量int不被视为常量。

4
我是一名有用的助手,可以为您翻译文本。
我写了一个使用CGAL的简单测试程序,但遇到了以下问题:
点被定义为上方所示。
typedef K::Point_d Point;

但我认为这并不相关。
当我尝试按以下方式编译程序时:
const int size = 10;
Point P[size];
毫无问题地执行此操作。
如果我试图编译其他内容:
const int size = stoi("10");
Point P[size]

我遇到了以下错误。
error: variable length array of non-POD element type 'Point' (aka 'Point_d<Cartesian_d<double,
  CGAL::Linear_algebraCd<double, std::__1::allocator<double> > > >')

为什么从字符串中检索大小时,大小被视为变量而不是常量?

6
Const并不表示常量,它表示一个运行时变量,无法被操作。你需要使用constexpr来声明一个常量。 - JVApen
因为stoi没有被标记为constexpr,而且它处理的是std::string,这可能不符合要求,因为std::string执行了许多在constexpr函数中不允许的操作(例如内存分配)。 - Matteo Italia
可能是无法使用变量设置变量长度的重复问题。 - Joseph D.
在一种情况下,该值在编译时已知。在另一种情况下,该值可能无法修改,但现在已在编译时知道。 - David Schwartz
6个回答

2

C++定义了特定的常量表达式,这些表达式被定义为在编译时已知 - 请参见constant_expression

一般来说,未定义为constexpr的函数无论你传递什么参数都不是常量表达式。您可以尝试强制将其视为这样:

#include<string>

constexpr int x = std::stoi("10");
int main() {
    return 0;
}

但你会发现这仍然会导致错误:
error: call to non-constexpr function 'int std::stoi(const string&, std::size_t*, int)'

很遗憾,stoi不是常量表达式,所以你没有办法使用它。如果你非常坚持,你可以使用自己的实现,例如在如何在编译时将C字符串转换为int?中提供了一种方法。


1
编译器不理解 stoi 函数的语义。它只看到你调用了一个返回整数的函数(无论这个函数是否可以内联和优化都是与语义分开的问题)。
对于编译器来说,以下两种写法在语义上几乎没有区别。
const int size = stoi("10");

const int size = getchar();

正如其他答案提到的那样,constexpr是个例外。我只是想说明一下这个问题。

1
第一个 const int size = 10; 是一个编译时常量表达式,可以在编译期计算,但是 const int size = stoi("10") 不是编译时常量表达式,因此无法编译。 std::stoi 不是一个可以在编译时评估的 constexpr 函数。
如果您想要这个功能,您可能需要创建一个 constexpr 函数来在编译时进行评估。
constexpr bool is_digit(char c) {
    return c <= '9' && c >= '0';
}

constexpr int stoi_impl(const char* str, int value = 0) {
    return *str ?
            is_digit(*str) ?
                stoi_impl(str + 1, (*str - '0') + value * 10)
                : throw "compile-time-error: not a digit"
            : value;
}

constexpr int to_int(const char* str) {
    return stoi_impl(str);
}


int main() {
    constexpr int size = to_int("10");
    int arr[size];
}

这将编译;[从这里复制]

1
在第一个代码示例中。
const int size = 10;
Point P[size];

变量size的特定值可以在编译时使用,因为它是已知的(您已经指定)。因此,编译器可以将其所有用法替换为特定值,而不实际创建变量。

在第二个示例中

const int size = stoi("10");
Point P[size]

编译器无法知道这个值,因为它是在运行时通过 stoi 函数推导出来的。因此,它无法替换数组的大小(必须事先知道以确定要分配多少内存),所以你会得到错误。

C++11 中有一个constexpr的技巧,允许一些声明为 constexpr 的函数在编译时进行评估。但在你的情况下,stoi 并不是一个 constexpr 函数,所以你无法使用这个特定函数实现你想要的效果。你可以实现自己的 constexpr stoi,但我觉得这没多大意义,因为在这种情况下,你的代码会包含类似这样的内容:my_constexpr_stoi("10"),也就是说,参数总是手动编写的,始终是事先已知的。为什么不直接写 10 呢?..


1

stoi不是在编译时评估的。这意味着编译器不知道数组应该有多大。 如果想要做类似的事情,您必须使用constexpr函数( constexpr function)。这些可以在编译时评估,然后它就可以工作了。


0

为了进一步解释kabanus的答案,语言标准规定C++11中的数组边界必须是所谓的“整数常量表达式”。C++17稍微放宽了这个限制,但现在并不重要。

cppreference上的这个页面列出了所有在常量表达式内不允许做的事情。它基本上引用了语言标准。

允许您第一个示例的相关部分是8a。除非是初始化为常量表达式的const变量,否则不允许使用变量。因此,在第一个示例中,使用初始化为常量表达式的整数类型const变量的名称是合法的。数字常量是常量表达式,因此size是初始化为常量表达式的const变量,这是完全可以的。

然而,由于第二点的原因,如果函数调用既被声明为constexpr又已经定义,则它们只是常量表达式。因此,为了使标准库函数能够在您的代码中作为数组表达式使用,它需要在头文件中被声明为constexpr并且也被定义。

标准并没有说stoi()符合要求,尽管我认为它并没有直接禁止某些实现提供它作为扩展。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接