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

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

这段代码失败了:

constexpr std::string constString = "constString";

错误:类型' const string {aka const std::basic_string} '的 constexpr变量“constString”不是字面意思…因为… ' std::basic_string '具有非平凡析构函数

可以在__abc1中使用std::string吗?(显然不是…)如果有,怎么做?是否有另一种方法在constexpr中使用字符串?

187831 次浏览

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

但是你可以这样做:

constexpr char constString[] = "constString";

在运行时,当需要时,可以使用它来构造std::string

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

对于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是一个类似__abc3的对象,它作为对任何char对象序列的不可变、非所有引用。

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;

除了all / most方法的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讨论在:不能创建constexpr std::vector

在Ubuntu 19.04中测试。

c++ 20朝着在编译时使用std::string迈进了一步,但P0980将不允许你编写像你的问题中的代码:

constexpr std::string constString = "constString";

原因是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

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

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

那么没有被时间计算释放的存储呢 完成吗?我们可以不允许,但确实有 这可能是可取的令人信服的用例。例如,这可能 成为更灵活的“字符串字面量”的基础;类。< >强我们 因此,提出如果非瞬态constexpr分配为 有效的(下面将进行描述),则将分配的对象提升为 静态存储持续时间.

.

有一种方法可以将暂态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";
}