c++ - std::string 的强 typedef
问题描述
动机(问题背景)
我使用 std::string 有很多含义。例如,地址和名称(在实践中更多的含义)。
假设地址和名称具有默认值。
void set_info(std::string address, std::string name) {
// set address and name
}
void set_info(std::string address) {
// set address and set default name
}
void set_info(std::string name) {
// set name and set default address
}
目标不仅是函数,而且是类构造函数。
struct info {
info(std::string address, std::string name)
: address_{std::move(address)}, name_{std::move(name)}
{}
info(std::string address)
: address_{std::move(address)}
{}
info(std::string name)
: name_{std::move(name)}
{}
std::string address_;
std::string name_;
};
但是,后两个重载是相同的(唯一的区别是参数名称)。所以我不能那样做。
所以我想出了 std::string 的强 typedef 有助于解决这种情况。
额外的动力
如果每个信息都有不同的类型,可以避免意外的类型匹配。我希望它可以帮助“难以滥用”的界面。动机类似于 Boost.Unit https://www.boost.org/doc/libs/1_76_0/doc/html/boost_units.html
问题
有什么好方法可以编写 std::string 的强 typedef 吗?
我的要求在这里:
- 可以编写多个强类型定义的重载。
void foo(type1) {} void foo(type2) {}
- 可以像 std::string 一样操作,包括辅助操作符。至少需要 operator+()。运算符的工作方式应该类似于 https://en.cppreference.com/w/cpp/string/basic_string/operator%2B operator+() 的返回值应该是强类型定义的类型,而不是 std::string。
我定义了以下类:
struct type1 : std::string {
using std::string::string;
};
struct type2 : std::string {
using std::string::string;
};
它适用于要求 1。但是,operator+() 的返回类型是 std::string,而不是强 typedef-ed 类型。
https://wandbox.org/permlink/6GAlcCoZi8XvumnZ
#include <string>
#include <iostream>
struct type1 : std::string {
using std::string::string;
};
struct type2 : std::string {
using std::string::string;
};
inline void foo(type1 const& v) {
std::cout << "type1:" << v << std::endl;
}
inline void foo(type2 const& v) {
std::cout << "type2:" << v << std::endl;
}
int main() {
type1 t1 = "ABC";
type2 t2 = "DEF";
foo(t1);
foo(t2);
// The return type of t1 + "123" is std::string, not type1
static_assert(std::is_same_v<decltype(t1 + "123"), std::string>);
// So it is not valid code to call foo(type1 const&)
// foo(t1 + "123");
}
我正在寻找一种方法来实现满足以下代码的 operator+():
int main() {
using namespace std::string_literals;
type1 m1 = "abc";
type2 m2 = "def";
// expected foo(type1 const&) is called
foo(m1);
foo(m1 + "a");
foo("a" + m1);
foo(m1 + "a"s);
foo("a"s + m1);
// expected foo(type2 const&) is called
foo(m2);
foo(m2 + "a");
foo("a" + m2);
foo(m2 + "a"s);
foo("a"s + m2);
std::string s = "a"s + "b"; // not become error
}
我试图实现类似 operator+() 的东西,但它会导致模棱两可的重载。
template <typename T>
inline
std::enable_if_t<
std::is_convertible_v<T, std::string> &&
(!std::is_base_of_v<std::string, std::decay_t<T>> ||
std::is_same_v<std::decay_t<T>, std::string>),
type1
>
operator+(type1 lhs, T&& rhs) {
lhs += std::forward<T>(rhs);
return lhs;
}
template <typename T>
inline
std::enable_if_t<
std::is_convertible_v<T, std::string> &&
(!std::is_base_of_v<std::string, std::decay_t<T>> ||
std::is_same_v<std::decay_t<T>, std::string>),
type1
>
operator+(T&& lhs, type1 const& rhs) {
type1 ret{std::forward<T>(lhs)};
ret += rhs;
return ret;
}
我想如果我为每个强类型定义类型(type1、type2、...)实现 operator+() 重载的所有组合,但我希望有更好的方法。
解决方案
您不需要包装 std::string 的每个操作,因为您不需要使用它们。调用者所要做的就是用正确的类型和内部字符串的值初始化参数:
struct address_type {
std::string value;
};
struct name_type {
std::string value;
};
struct info {
info(address_type address, name_type name)
: address_{std::move(address.value)}, name_{std::move(name.value)}
{}
info(address_type address)
: address_{std::move(address.value)}
{}
info(name_type name)
: name_{std::move(name.value)}
{}
private:
std::string address_;
std::string name_;
};
info with_address {address_type{"str"}};
info with_name {name_type {"str"}};
但是,如果您想继续使用自定义类型提供字符串功能的方法,那么您必须包装所有操作。这将是大量的样板文件,没有什么魔法可以避免这种情况。
推荐阅读
- azure-active-directory - Azure B2C - 自定义策略 - 连接身份提供者时出错
- python - Python regex - 将名称与合法形式匹配
- java - 地图结构
/到 / 映射 - c++ - 移动智能指针两次与复制
- cybersource - 无法从沙盒复制支付工具令牌创建操作,出现“无效的配置文件所有者”错误
- javascript - 在饼图上显示标签而不是数据值 Chart.js
- python - teensy bord 与系统之间使用 python 进行串行通信
- mysql - 优化运行时间更长的mysql查询
- ios - 反应原生:未找到活动的 iOS 设备
- architecture - 添加实体和列数据库的新属性会破坏 OCP(开放封闭原则)吗?