c++ 可以在常量表达式中使用std::string吗?

vmpqdwk3  于 8个月前  发布在  其他
关注(0)|答案(5)|浏览(77)
  • 使用C++11,Ubuntu 14.04,GCC默认工具链 *。

此代码失败:

constexpr std::string constString = "constString";

错误:constexpr变量“constString”的类型“const string {aka const std::basic_string}”不是文字.因为.'std::basic_string'有一个非平凡的析构函数
可以在constexpr中使用std::string吗?(显然不是......)如果是,怎么做?在constexpr中使用字符串有其他方法吗?

lrl1mhuk

lrl1mhuk1#

C++20开始,是的,但只有当std::string在常量计算结束时被销毁时。因此,虽然你的例子仍然无法编译,但像这样的东西会:

constexpr std::size_t n = std::string("hello, world").size();

但是,从C++17开始,您可以使用string_view

constexpr std::string_view sv = "hello, world";

string_view是一个类似string的对象,它充当对任何char对象序列的不可变、非所有权引用。

1cosmwyk

1cosmwyk2#

不,你的编译器已经给了你一个全面的解释。
但你可以这样做:

constexpr char constString[] = "constString";

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

rpppsulh

rpppsulh3#

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

下面的建议has been accepted显然: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;

除了所有/大多数方法的constexpr版本之外。
从GCC 9.1.0开始不支持,以下代码无法编译:

#include <string>

int main() {
    constexpr std::string s("abc");
}

使用:

g++-9 -std=c++2a main.cpp

错误:

error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘s’ is not literal

std::vector讨论于:Cannot create constexpr std::vector
在Ubuntu 19.04中测试。

jtw3ybtb

jtw3ybtb4#

由于问题是非平凡析构函数,因此如果从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;
}
vhmi4jdf

vhmi4jdf5#

C++20是使在编译时使用std::string成为可能的一步,但P0980不允许您编写类似问题中的代码:

constexpr std::string constString = "constString";

原因是constexprstd::string只允许在constexpr函数中使用(常量表达式求值上下文)。constexprstd::string分配的内存必须在这样的函数返回之前被释放-这就是所谓的 transient 分配,并且这些内存不能“泄漏”到运行时之外,在运行时可以访问constexpr对象(存储在数据段中)。例如,在当前VS 2022预览版(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

这是因为它试图进行不允许的非瞬时分配-这意味着分配到编译二进制文件的数据段中。
在p0784 r1中的“非 transient 分配”段落中,您可以发现有一个允许将 transient 内存转换为静态内存的计划(强调我的):
在评估完成时尚未释放的存储怎么办?我们可以不允许这样做,但确实有一些令人信服的用例,这可能是可取的。例如,这可能是一种更灵活的“字符串文字”类的基础。因此,我们建议,如果非 transient constexpr分配是有效的(将在下面描述),则将分配的对象提升到静态存储持续时间
有一种方法可以将 transient std::string数据导出到外部,使其在运行时可用。你必须把它复制到std::array,问题是要计算std::array的最终大小,你可以预先设置一些大的大小或计算std::string两次-一次得到大小,然后得到实际数据。以下代码成功编译并在当前的VS 2022预览版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";
}

相关问题