是否可以在constexpr中使用std :: string?

Vec*_*tor 142 stdstring constexpr c++11

使用C++ 11,Ubuntu 14.04,GCC默认工具链.

此代码失败:

constexpr std::string constString = "constString";
Run Code Online (Sandbox Code Playgroud)

错误:constexpr变量'constString'的类型'const string {aka const std :: basic_string}'不是文字...因为......'std :: basic_string'有一个非平凡的析构函数

是否有可能使用std::stringconstexpr?(显然不是......)如果是这样,怎么样?是否有另一种方法在一个字符串中使用字符串constexpr

ten*_*our 139

不,你的编译器已经给你一个全面的解释.

但你可以这样做:

constexpr char constString[] = "constString";
Run Code Online (Sandbox Code Playgroud)

在运行时,这可以用于std::string在需要时构造.

  • 为什么不`constexpr auto constString ="constString";`?不需要使用那种丑陋的数组语法;-) (69认同)
  • 在这个问题的背景下,它更清楚.我的观点是你可以选择哪种字符串类型.当我试图强调要使用的数据类型时,`char []`比`auto`更详细/清晰. (61认同)
  • @tenfour对,这是一个好点.我想我有时太专注于使用`auto` ;-) (6认同)
  • @stefan 通常我更喜欢 `auto`。但是有一个细微的区别:`auto` 将被推导出为 `const char* const`,因此 `sizeof(constString)` 将产生 `8`。然而,使用 `char constString[]` 语法,`sizeof(constString)` 产生字符串文字中的字符数,这大概是人们所期望的。 (6认同)
  • 在该上下文中constexpr char数组是否有意义?如果你用它来构造一个字符串,那么无论如何它都会被复制.将文字传递给字符串的构造函数并将这样的constexpr数组传递给它有什么区别? (4认同)
  • `std::string_view` 应优先于 char 数组,以避免隐式转换为 `std::string`。如果您已经需要将其作为“std::string”传递,那么最好将其定义为“const std::string”或“static const std::string” (2认同)

Jos*_*son 112

在C++ 17中,您可以使用string_view:

constexpr std::string_view sv = "hello, world";
Run Code Online (Sandbox Code Playgroud)

A string_view是类似string对象,充当对任何char对象序列的不可变的非拥有引用.

  • @RamboRamon`tring_view`不能隐式转换为`string`,因此从`string_view`意外构造`string`几乎没有危险.相反,`char const*`_is_可以隐式转换为`string`,所以在这种意义上使用`string_view`实际上更安全. (20认同)
  • 请注意,虽然“std::string”有一个“.c_str()”方法,该方法返回一个以“NULL”结尾的“char*”,但“string_view”*不会*。与“std::string”类似,它有一个“.data()”方法,该方法返回一个“char*”,该“char*”*不*保证以 null 终止(并且不会是“string_view”是一个视图另一个没有内部“NULL”的字符串)。如果您从编译时常量“char”数组初始化它,它*将*以“NULL”终止,但如果您需要使用系统调用,请小心接受“string_view”。 (4认同)
  • 请注意,只要将此常量传递给采用`const std :: string&`的函数,就必须构造一个新的std :: string。通常这与创建常量时所想到的相反。因此,我倾向于说这不是一个好主意。至少你要小心。 (3认同)
  • 感谢您的澄清。我完全同意并且确实忘记了string_view不能隐式转换为string。IMO我提出的问题仍然有效,但不适用于`string_view`。实际上,正如您提到的那样,在这方面更安全。 (3认同)
  • 如果这个答案更多地说明了`string_view`是什么,而不仅仅是一个链接,那将会很棒. (3认同)

neu*_*ont 16

由于问题是非平凡的析构函数,因此如果从中删除析构函数std::string,则可以定义constexpr该类型的实例.像这样

struct constexpr_str {
    char const* str;
    std::size_t size;

    // can only construct from a char[] literal
    template <std::size_t N>
    constexpr constexpr_str(char const (&s)[N])
        : str(s)
        , size(N - 1) // not count the trailing nul
    {}
};

int main()
{
    constexpr constexpr_str s("constString");

    // its .size is a constexpr
    std::array<int, s.size> a;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 这基本上就是 c++17 `string_view` 是什么,除了 `string_view` 为你提供了你从 `std::string` 中知道的大部分功能 (13认同)

mar*_*inj 13

C++20 是朝着可以在编译时使用的方向迈出的一步std::string,但 P0980 不允许您编写像问题中那样的代码:

constexpr std::string constString = "constString";
Run Code Online (Sandbox Code Playgroud)

原因是constexpr std::string它只允许在函数(常量表达式求值上下文)中使用constexpr。分配的内存constexpr std::string必须在此类函数返回之前释放 - 这就是所谓的瞬时分配,并且该内存不能“泄漏”到运行时之外的运行时可constexpr访问的对象(存储在数据段中)。例如,在当前 VS2022 预览版(cl 版本:19.30.30704)中编译上述代码行会导致以下错误:

1> : error C2131: expression did not evaluate to a constant
1> : message : (sub-)object points to memory which was heap allocated during constant evaluation
Run Code Online (Sandbox Code Playgroud)

这是因为它试图进行不允许的非瞬态分配 - 这意味着分配到已编译二进制文件的数据段中。

在p0784r1中,在“非瞬态分配”段落中,您可以发现有一个计划允许将瞬态内存转换为静态内存(强调我的):

评估完成时尚未释放的存储怎么办?我们可以不允许这样做,但确实存在令人信服的用例,这可能是可取的。例如,这可能是更灵活的“字符串文字”类的基础。因此,我们建议,如果非瞬态常量表达式分配有效(接下来将进行描述),则分配的对象将提升为静态存储持续时间

有一种方法可以将瞬态std::string数据导出到外部以使其在运行时可用。您必须将其复制到std::array,问题是计算 的最终大小std::array,您可以预设一些较大的大小或计算std::string两次 - 一次获取大小,然后获取实际数据。以下代码在当前 VS2022 预览版 5 上成功编译并运行。它基本上通过单词之间的分隔符连接三个单词:

constexpr auto join_length(const std::vector<std::string>& vec, char delimiter) {
  std::size_t length = std::accumulate(vec.begin(), vec.end(), 0,
    [](std::size_t sum, const std::string& s) {
      return sum + s.size();
    });
  return length + vec.size();
}

template<size_t N>
constexpr std::array<char, N+1> join_to_array(const std::vector<std::string>& vec, char delimiter) {
  std::string result = std::accumulate(std::next(vec.begin()), vec.end(),
    vec[0],
    [&delimiter](const std::string& a, const std::string& b) {
      return a + delimiter + b;
    });
  std::array<char, N+1> arr = {};
  int i = 0;
  for (auto c : result) {
    arr[i++] = c;
  }
  return arr;
}
constexpr std::vector<std::string> getWords() {
  return { "one", "two", "three" };
}

int main()
{
  constexpr auto arr2 = join_to_array<join_length(getWords(), ';')>(getWords(), ';');
  static_assert(std::string(&arr2[0]) == "one;two;three");
  std::cout << &arr2[0] << "\n";
}
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 6

C ++ 20将添加constexpr字符串和向量

显然已接受以下提议:http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0980r0.pdf并添加了以下构造函数:

// 20.3.2.2, construct/copy/destroy
constexpr
basic_string() noexcept(noexcept(Allocator())) : basic_string(Allocator()) { }
constexpr
explicit basic_string(const Allocator& a) noexcept;
constexpr
basic_string(const basic_string& str);
constexpr
basic_string(basic_string&& str) noexcept;
Run Code Online (Sandbox Code Playgroud)

除了所有/大多数方法的constexpr版本。

自GCC 9.1.0起不提供支持,以下内容无法编译:

#include <string>

int main() {
    constexpr std::string s("abc");
}
Run Code Online (Sandbox Code Playgroud)

与:

g++-9 -std=c++2a main.cpp
Run Code Online (Sandbox Code Playgroud)

错误:

error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘s’ is not literal
Run Code Online (Sandbox Code Playgroud)

std::vector讨论在:无法创建constexpr std :: vector

在Ubuntu 19.04中测试。