有很多问题:
- 执行
storedString += i;(其中i 是int)并不像您认为的那样做
- 您未能从
operator()(std::string const&) const 返回值
- 你递归到
create_string(),这是一个临时的;这就是你无法正确制作东西的原因const
使用 mutable/const 解决所有问题并正确处理:
Live On Coliru
#include <boost/variant.hpp>
#include <vector>
#include <iostream>
typedef boost::make_recursive_variant<std::string, int, std::vector<boost::recursive_variant_> >::type ObjectE;
typedef std::vector<ObjectE> ObjectV;
struct create_string : boost::static_visitor<std::string const&> {
std::string const& operator()(const int i) {
return storedString += std::to_string(i) + ", ";
}
std::string const& operator()(ObjectV const &v) {
storedString += "[ ";
for (std::size_t i = 0; i < v.size() - 1; i++) {
create_string nest;
storedString += boost::apply_visitor(nest, v[i]) + ", ";
}
create_string nest;
storedString += boost::apply_visitor(nest, v[v.size() - 1]);
return storedString += " ]";
}
std::string const& operator()(std::string const &s) {
return storedString += s + ", ";
}
std::string storedString = "";
};
int main() {
ObjectE obj = ObjectV {
1,
"hello world",
ObjectV {
42, ObjectV { "some more" }, -42
},
};
create_string vis;
std::cout << "Result '" << boost::apply_visitor(vis, obj) << "'\n";
}
打印:
Result '[ 1, , hello world, , [ 42, , [ some more, ], -42, ] ]'
改进
你可以做得更好。
首先,您可以通过使用流而不是复制字符串来减少分配。这也可以使访问者无国籍。
您还可以让访问者直接应用于变体,因此您不必再调用apply_visitor。
-
您可以避免多余的逗号并使用std::quoted 正确转义字符串值(如果它们包含"、[、]、,)
李>
您可以使所有内容都通用,以便与更多变体一起使用
以下是这些项目符号的实现:
Live On Coliru
#include <boost/variant.hpp>
#include <vector>
#include <iostream>
#include <iomanip>
typedef boost::make_recursive_variant<std::string, int, std::vector<boost::recursive_variant_> >::type ObjectE;
typedef std::vector<ObjectE> ObjectV;
struct printer {
using result_type = void;
std::ostream& _os;
// forwards for `operator()`
template <typename T> void call(T const& v) const { return operator()(v); }
// dispatch for variants
template <typename... Ts> void operator()(boost::variant<Ts...> const& v) const {
return boost::apply_visitor(*this, v);
}
void operator()(int i) const { _os << i; }
void operator()(std::string const &s) const { _os << std::quoted(s); }
template <typename... Ts> void operator()(std::vector<Ts...> const& v) const {
_os << "[ ";
bool first = true;
for (auto& el : v) {
if (first) first = false;
else _os << ", ";
call(el);
}
_os << " ]";
}
};
int main() {
ObjectE obj = ObjectV {
1,
"hello world",
ObjectV {
42, ObjectV { "some more" }, -42
},
};
printer print{std::cout};
print(obj);
}
打印
[ 1, "hello world", [ 42, [ "some more" ], -42 ] ]
更多:漂亮的缩进
现在我们已经丢失了字符串的包袱,我们可以再次为访问者添加一些状态。让我们用缩进让它打印出来。
Live On Coliru
struct printer {
using result_type = void;
std::ostream& _os;
std::string _indent = "";
template <typename... Ts> void operator()(boost::variant<Ts...> const& v) const {
return boost::apply_visitor(*this, v);
}
void operator()(int i) const { _os << i; }
void operator()(std::string const &s) const { _os << std::quoted(s); }
template <typename... Ts> void operator()(std::vector<Ts...> const& v) const {
_os << "[";
auto indent = _indent + " ";
bool first = true;
for (auto& el : v) {
if (first) first = false;
else _os << ",";
_os << "\n" << indent;
printer{_os, indent}(el);
}
if (!v.empty())
_os << "\n" << _indent;
_os << "]";
}
};
打印
[
1,
"hello world",
[],
[
42,
[
"some more"
],
-42
]
]
为了胜利:没有递归变体,整洁的文字
我注意到您编写的伪代码更像 JSON:
ObjectE t = ["string" , [1, 2] , ["str2", "str3"], [1,3,6], [[1,2], [1,4]]]
那不是 C++。但是,我可以让它像这样工作:
ObjectE t = {"string" , {1, 2} , {"str2", "str3"}, {1,3,6}, {{1,2}, {1,4}}};
你可以通过创建一个派生自变量的类型,并添加一个构造函数来获取向量元素的初始化列表:
struct ObjectE : boost::variant<std::string, int, std::vector<ObjectE> > {
using Base = boost::variant<std::string, int, std::vector<ObjectE> >;
using Base::variant;
using Base::operator=;
ObjectE(std::initializer_list<ObjectE> init) : Base(std::vector<ObjectE>(init)) {}
};
就是这样。上面改进的访问者从一开始就是通用的足够,所以我们不需要更改另一行代码:
Live On Coliru
printer print{std::cout};
print(ObjectE { 1, "hello world", {}, { 42, { "some more" }, -42 } });
std::cout << "\nt: ";
ObjectE t = {"string" , {1, 2} , {"str2", "str3"}, {1,3,6}, {{1,2}, {1,4}}};
print(t);
打印
[
1,
"hello world",
[],
[
42,
[
"some more"
],
-42
]
]
t: [
"string",
[
1,
2
],
[
"str2",
"str3"
],
[
1,
3,
6
],
[
[
1,
2
],
[
1,
4
]
]
]