std::string 的强类型定义
strong typedef of std::string
动机(问题背景)
我用 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
}
目标不仅是函数而且是class构造函数。
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 的强类型定义来帮助解决这个问题。
额外的动力
如果每个信息都有不同的类型,可以避免意外的类型匹配。我希望它可以帮助“难以滥用”的界面。
动机类似于 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+() 的 return 值应该是强类型定义类型,而不是 std::string.
我定义了以下 classes:
struct type1 : std::string {
using std::string::string;
};
struct type2 : std::string {
using std::string::string;
};
它很适合需求 1。但是,operator+() 的 return 类型是 std::string,不是强类型定义类型。
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"}};
但是,如果您想继续使用自定义类型提供字符串功能的方法,则必须包装所有操作。这将是一大堆样板文件,没有什么魔法可以避免。
动机(问题背景)
我用 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
}
目标不仅是函数而且是class构造函数。
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 的强类型定义来帮助解决这个问题。
额外的动力
如果每个信息都有不同的类型,可以避免意外的类型匹配。我希望它可以帮助“难以滥用”的界面。 动机类似于 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+() 的 return 值应该是强类型定义类型,而不是 std::string.
我定义了以下 classes:
struct type1 : std::string {
using std::string::string;
};
struct type2 : std::string {
using std::string::string;
};
它很适合需求 1。但是,operator+() 的 return 类型是 std::string,不是强类型定义类型。
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"}};
但是,如果您想继续使用自定义类型提供字符串功能的方法,则必须包装所有操作。这将是一大堆样板文件,没有什么魔法可以避免。