由于std::format并非所有地方都支持,而且我不想要另一個大的依賴項fmt,所以我想快速推出我自己的to_string解決方案,用于多種型別。以下是代碼。
#include <ranges>
#include <string>
#include <concepts>
template<typename Type>
constexpr std::string stringify(const Type &data) noexcept;
template<typename Type> requires std::integral<Type>
constexpr std::string stringify(const Type &data) noexcept {
return std::to_string(data);
}
template<typename Type>
constexpr std::string stringify_inner(const Type &data) noexcept {
return stringify(data);
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify_inner(const Type &data) noexcept {
return "[" stringify(data) "]";
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify(const Type &data) noexcept {
std::string string;
for (auto &i : data) {
string = stringify_inner(i);
string = ", ";
}
string.pop_back();
string.pop_back();
return string;
}
現在,如果我撰寫以下代碼,我會得到一些不錯的輸出。
int main() {
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<std::vector<int>> b = {{ 1, 2 }, { 3, 4 }};
std::cout << stringify(a) << std::endl;
std::cout << stringify(b) << std::endl;
}
// >>> 1, 2, 3, 4
// >>> [1, 2], [3, 4]
現在,由于某種原因,如果我洗掉stringify<std::vector<int>>呼叫,編譯器將無法推斷出正確的函式。
int main() {
// std::vector<int> a = { 1, 2, 3, 4 };
std::vector<std::vector<int>> b = {{ 1, 2 }, { 3, 4 }};
// std::cout << stringify(a) << std::endl;
std::cout << stringify(b) << std::endl;
}
// >>> undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>,
// >>> std::allocator<char> > stringify<std::vector<int, std::allocator<int> > >(std::vector<int,
// >>> std::allocator<int> > const&)'
我想我明白這里發生了什么,但我不知道為什么或如何解決它。似乎編譯器需要手動實體化stringify<std::vector<int>>,以便它可以決議stringify<std::vector<std::vector<int>>>.
我以前從未遇到過這種行為,也不知道如何繼續。我正在使用 C 20 編譯,在 Windows 上使用 GCC。謝謝。
uj5u.com熱心網友回復:
模板多載的宣告順序導致
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify(const Type& data) noexcept;
專攻時的超載
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify_inner(const Type &data) noexcept {
return "[" stringify(data) "]";
}
with Type = std::vector<int>,但此函式未在任何地方定義。您需要確保足夠早地為范圍宣告函式簽名,以便編譯器使用它:
template<typename Type>
constexpr std::string stringify(const Type& data) noexcept;
template<typename Type> requires std::integral<Type>
constexpr std::string stringify(const Type& data) noexcept {
return std::to_string(data);
}
/////////////////////// Add this ////////////////////////////////////
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify(const Type& data) noexcept;
/////////////////////////////////////////////////////////////////////
template<typename Type>
constexpr std::string stringify_inner(const Type& data) noexcept {
return stringify(data);
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify_inner(const Type& data) noexcept {
return "[" stringify(data) "]";
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify(const Type& data) noexcept {
std::string string;
for (auto& i : data) {
string = stringify_inner(i);
string = ", ";
}
string.pop_back();
string.pop_back();
return string;
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify(const Type& data) noexcept;
template<typename Type>
constexpr std::string stringify_inner(const Type& data) noexcept {
return stringify(data);
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify_inner(const Type& data) noexcept {
return "[" stringify(data) "]";
}
template<typename Type> requires std::ranges::range<Type>
constexpr std::string stringify(const Type& data) noexcept {
std::string string;
for (auto& i : data) {
string = stringify_inner(i);
string = ", ";
}
string.pop_back();
string.pop_back();
return string;
}
uj5u.com熱心網友回復:
答案在cppreference上以散文形式輸入
必須在第一次使用之前宣告會導致隱式實體化的特化
在您的示例中,stringifyfor ranges 的特化將通過呼叫第一個 ofstringify_inner多載來實作,但它是在它之后宣告的,而不是之前宣告的。
正如經常發生的那樣,我們可以通過查看 clang 對代碼的看法來獲得一些見解
Clangd does give a warning for this: `inline function 'stringify<std::vector<int>>' is not defined [-Wundefined-inline]`. constexpr std::string stringify(const Type &data) noexcept; ^ somesource.cpp:22:18: note: used here return "[" stringify(data) "]"; ^ 1 warning generated.
這至少比 GCC 更清楚。
相關問答和對另一個答案的相關評論。
uj5u.com熱心網友回復:
其他答案提到了多載和函式宣告問題。
(對于未來的讀者)我建議在單個(遞回)函式中進行字串化,該函式負責范圍,并且std::integral(或is_stringable)多載可以保留用于整數型別。
如下所示:
#include <type_traits>
#include <string>
#include <concepts>
#include <ranges>
using namespace std::string_literals;
template<typename Type> // concept for checking std::to_string-able types
concept is_stringable = requires (Type t)
{ {std::to_string(t) }->std::same_as<std::string>; };
// "stringify" overload for is_stringable
constexpr std::string stringify(const is_stringable auto& data) {
return std::to_string(data);
}
// "stringify" overload for ranges
constexpr std::string stringify(const std::ranges::range auto& data) {
// value type of the ranges (Only Sequence ranges)
using ValueType = std::remove_const_t<
std::remove_reference_t<decltype(*data.cbegin())>
>;
if constexpr (is_stringable<ValueType>) {
std::string string{};
for (ValueType element : data)
string = stringify(element) ", "s;
string.pop_back();
string.pop_back();
return "["s string "]"s;
}
// else if constexpr (<other types Ex. ValueType == std::tuple>) {}
// .... more
else {
std::string string;
for (const ValueType& innerRange : data)
string = stringify(innerRange);
return string;
}
}
在 godbolt.org 上查看現場演示
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/519909.html
