我有一個簡單的 C 20 編譯時字串型別,其定義如下:
template<std::size_t N>
class compile_time_string_storage
{
public:
constexpr compile_time_string_storage(std::array<char, N> str) noexcept
: value(str)
{
}
constexpr compile_time_string_storage(const char (&str)[N]) noexcept
{
std::copy_n(str, N, value.begin());
}
constexpr compile_time_string_storage(char c) noexcept
: value{ c }
{
}
std::array<char, N> value;
};
compile_time_string_storage(char)->compile_time_string_storage<1>;
template<compile_time_string_storage S>
class str
{
public:
[[nodiscard]] constexpr static std::string_view get() noexcept
{
return { S.value.data(), size_ };
}
[[nodiscard]] constexpr static std::size_t size() noexcept { return size_; }
private:
constexpr static std::size_t calculate_size() noexcept
{
auto count = std::size_t{ 0 };
for (; count < S.value.size(); count) {
if (S.value[count] == '\0') {
break;
}
}
return count;
}
constexpr static std::size_t size_ = calculate_size();
};
這行得通。但是,我想通過 增加對構造的支持,std::string_view原則上它應該很簡單,因為它可以被 constexpr 構造,但我正在努力。
compile_time_string_storage最初我用推導指南添加了一個 constexpr 建構式多載:
<source>:55:80: error: non-type template argument is not a constant expression
compile_time_string_storage(std::string_view str)->compile_time_string_storage<str.size()>;
^~~~~~~~~~
建構式引數不能在編譯時背景關系中使用。然后我嘗試使用演繹指南作為我可以專攻的“標簽”:
constexpr auto string_view_tag = std::numeric_limits<std::size_t>::max();
template<>
class compile_time_string_storage<string_view_tag>
{
public:
constexpr compile_time_string_storage(std::string_view str) : value(str)
{}
std::string_view value;
};
compile_time_string_storage(std::string_view)->compile_time_string_storage<string_view_tag>;
不,不能使用那個:
<source>:52:22: note: 'compile_time_string_storage<string_view_tag>' is not a structural type because it has a non-static data member of non-structural type 'std::string_view' (aka 'basic_string_view<char>')
std::string_view value;
^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c /11.2.0/string_view:510:18: note: 'std::string_view' (aka 'basic_string_view<char>') is not a structural type because it has a non-static data member that is not public
size_t _M_len;
^
好的,如果我制作一個等效的“結構”版本std::string_view呢?
struct str_view
{
constexpr str_view(std::string_view str) : data_{str.data()}, size_{str.size()}
{}
constexpr const char* const data() const noexcept { return data_; }
constexpr std::size_t size() const noexcept { return size_; }
const char* data_;
std::size_t size_;
};
template<>
class compile_time_string_storage<string_view_tag>
{
public:
constexpr compile_time_string_storage(std::string_view str) : value(str)
{}
str_view value;
};
不。
<source>:88:23: error: pointer to subobject of string literal is not allowed in a template argument
static_assert(str<"hello"sv>::get() == "hello");
^
有沒有辦法使用 astd::string_view來初始化compile_time_string_storage?
我目前正在使用推導指南為N(由編譯器定義設定)設定一個固定值,其作業原理是編譯器足夠智能,可以將原始字串資料保留在靜態存盤中而不是std::array內容中。但這是 hack,因為您需要預先知道所需的最大字串長度,但這是不對的。
uj5u.com熱心網友回復:
有沒有辦法使用 a
std::string_view來初始化compile_time_string_storage?
你的問題是你似乎想要很多東西,但這些東西不能同時存在。你要:
compile_time_string_storage從 a創建 aconstexprstring_view而不明確指定大小作為模板引數。- 為
compile_time_string_storage字串提供一個存盤 N 個字符的陣列。
你不能兩者兼得。只要compile_time_string_storage有一個size模板引數必須填寫一個非模板引數的屬性,這個就不行了。函式引數及其成員不是constexpr.
你的最后一次嘗試掉落了#2,這種方法可以奏效。但是,您遇到了一個單獨的問題:指向字串文字的指標不能用作模板引數。您也不能將它們作為其他物件的成員走私到模板引數中。
如果您使用字串堆疊陣列作為string_view. 當然,那么您的“compile_time_string_storage` 將是一個謊言,因為它實際上不會存盤字串。它會參考現有的存盤空間,這些存盤空間在有人使用該字串時可能存在也可能不存在。
如果您的最終目標需要能夠在不復制字符的情況下將字串文字指標推入此類,請放棄。標準明確禁止這樣做。所以你只剩下讓字串存盤一個陣列。
大小不能來自函式引數的陣列。
因此,您唯一的辦法就是手動執行此操作:獲取constexpr string_view,獲取其大小,并將該大小作為模板引數顯式傳遞:
constexpr compile_time_string_storage<some_constexpr_string_view.size()> var(some_constexpr_string_view);
這很丑陋,但這是你唯一的選擇。
或者,string_view完全避免并使用span<char const, N>從字串文字構造的。span能夠將跨度的大小維護為模板引數,這反過來又允許您compile_time_string_storage提取它而無需手動插入模板引數。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/537515.html
標籤:C 模板
