假设我有一个存储一些数据的类,
class Value {
public:
enum class Type {
int_type,
float_type,
double_type,
bool_type
};
friend bool operator==(const Value& lhs, const Value& rhs) {
// how to make this function clean and concise?
}
private:
void* ptr;
Type type;
};
Run Code Online (Sandbox Code Playgroud)
ptr指向基础值并type指示ptr应如何转换。
为了比较Value对象,我绝对可以列出所有可能的类型组合,但是代码将很难维护。喜欢:
if (lhs.type == Type::int_type && rhs.type == Type::float_type) {
return *static_cast<int*>(lhs.ptr) == *static_cast<float*>(rhs.type);
}
Run Code Online (Sandbox Code Playgroud)
任何想法如何减少复杂性?
更新:
我希望此类为动态类型,这意味着我可以执行以下操作:
Value v("abc"); // v is now a string
v = 123; // v is now an int
bool b = (v == 123.0); // b is true
Run Code Online (Sandbox Code Playgroud)
因此,我认为模板可能不会有所帮助。
实际上,您想要做的是编写弱类型。
此类类型是 python 和 javascript 等脚本语言的基础。
编写这样的类型比您最初想象的要复杂。
但是,一旦定义了转换矩阵,事情就会变得更容易(例如,比较字符串与布尔值的规则是什么?)
这是一个开始,使用std::variant:
#include <variant>
#include <string>
#include <type_traits>
#include <algorithm>
#include <cassert>
//
// Step 1 - define your conversion rules
// this is not as trivial as you might think
//
template<class To>
struct convert;
template<>
struct convert<int>
{
template<class From>
auto operator()(From const& from) const -> int
{
return int(from);
}
auto operator()(std::string const& from) const -> int
{
return std::atoi(from.c_str());
}
};
template<>
struct convert<double>
{
template<class From>
auto operator()(From const& from) const -> double
{
return double(from);
}
auto operator()(std::string const& from) const -> double
{
return std::atof(from.c_str());
}
};
template<>
struct convert<bool>
{
template<class From>
auto operator()(From const& from) const -> bool
{
return bool(from);
}
auto operator()(std::string from) const -> bool
{
auto lcase = [](auto ch) { return std::tolower(ch); };
std::transform(from.begin(), from.end(), from.begin(), lcase);
if (from == "true" || from == "yes" || std::atoi(from.c_str()))
return true;
else
return false;
}
};
template<>
struct convert<std::string>
{
template<class From>
auto operator()(From const& from) const -> std::string
{
return std::to_string(from);
}
auto operator()(bool const& from) const -> std::string
{
auto result = std::string();
if (from)
result.assign("true");
else
result.assign("false");
return result;
}
auto operator()(std::string const& from) const -> std::string const&
{
return from;
}
};
//
// Step 2 - use a std::variant
//
struct Value
{
explicit Value(int arg): store_(arg) {}
explicit Value(double arg): store_(arg) {}
explicit Value(bool arg): store_(arg) {}
explicit Value(std::string arg): store_(std::move(arg)) {}
explicit Value(const char* arg): store_(std::string(arg)) {}
friend bool operator==(const Value& lhs, const Value& rhs)
{
auto compare = [](auto &&l , auto&& r)
{
using l_type = std::decay_t<decltype(l)>;
auto conv = convert<l_type>();
return l == conv(r);
};
return std::visit(compare, lhs.store_, rhs.store_);
}
private:
using storage_type = std::variant<int, double, bool, std::string>;
private:
storage_type store_;
};
int main()
{
auto vt = Value(true);
auto vst = Value("true");
assert(vt == vst);
}
Run Code Online (Sandbox Code Playgroud)