我经常遇到这种情况(在我的C++/C++ 11代码中),其中我的类型基本上像内置类型(或类似的"基本简单"类型std::string),但其含义超过32位号或一堆字符.我没有在互联网上找到任何有用的东西,因为我真的没有什么条件可以搜索...
例子:
std::strings(首先可能不是最好的主意,但这是一个不同的故事).但真正糟糕的是,这些ID以std::strings或const char*s的形式通过系统传递.因此,在搜索类型时,很难(不可能)判断代码库ID中的位置.变量名称是ID(ID,id,Id)或密钥的全部变体,或者只是i或名称或其他.所以你也不能按名字搜索.所以我更愿意将这些变量作为类型传递id_t.uint16_ts.但我想把它们作为network_port_ts 传递给我们.我通常使用typedef使事情变得更好.这种方法有许多问题:
std::string而不是id_t).我尝试使用网络端口示例的另一件事是编写一个瘦的包装类运动operator uint16_t.这解决了前向声明的问题.但后来我遇到了一些陷阱,其中包含一些内部使用printf的日志记录宏.printfs仍然工作(好,编译),但没有打印端口号,但(我认为)对象的地址.
我认为尺寸像重量或长度Boost.Units可能值得一看(即使它看起来有点"沉重").但是对于上面的两个例子,它并不合适.
实现我想要的最佳实践是什么(使用Boost是一种选择)?
简而言之: 我想要实现的是将"具有更高意义的类型"作为自己的类型而不是普通的原始/低级/非抽象类型.(有点像)具有用户定义的类型.优选地,没有为具有基本相同的实现的每种类型编写完整类的巨大开销,仅仅能够执行已经可以执行的内置操作.
您可以使用BOOST_STRONG_TYPEDEF来获得一些便利.
它确实使用宏,我认为你可以进行异构比较(例如id == "123").
有两个版本,一定要从Boost Utility中选择一个版本.
对于字符串,您可以使用调味字符串欺骗系统(发明人:R.Martinho Fernandes).
这可以利用这样一个事实:您实际上可以改变a上的特征std::basic_string,并创建实际上不同的标记别名:
#include <string>
#include <iostream>
namespace dessert {
template <typename Tag>
struct not_quite_the_same_traits : std::char_traits<char> {};
template <typename Tag>
using strong_string_alias = std::basic_string<char, not_quite_the_same_traits<Tag>>;
using vanilla_string = std::string;
using strawberry_string = strong_string_alias<struct strawberry>;
using caramel_string = strong_string_alias<struct caramel>;
using chocolate_string = strong_string_alias<struct chocolate>;
template <typename T>
struct special;
template <typename T>
using special_string = strong_string_alias<special<T>>;
std::ostream& operator<<(std::ostream& os, vanilla_string const& s) {
return os << "vanilla: " << s.data();
}
std::ostream& operator<<(std::ostream& os, strawberry_string const& s) {
return os << "strawberry: " << s.data();
}
std::ostream& operator<<(std::ostream& os, caramel_string const& s) {
return os << "caramel: " << s.data();
}
std::ostream& operator<<(std::ostream& os, chocolate_string const& s) {
return os << "chocolate: " << s.data();
}
template <typename T>
std::ostream& operator<<(std::ostream& os, special_string<T> const& s) {
return os << "special: " << s.data();
}
}
int main() {
dessert::vanilla_string vanilla = "foo";
dessert::strawberry_string strawberry = "foo";
dessert::caramel_string caramel = "foo";
dessert::chocolate_string chocolate = "foo";
std::cout << vanilla << '\n';
std::cout << strawberry << '\n';
std::cout << caramel << '\n';
std::cout << chocolate << '\n';
dessert::special_string<struct nuts> nuts = "foo";
std::cout << nuts << '\n';
}
Run Code Online (Sandbox Code Playgroud)
要创建一个不是整数的整数(或者不是字符串的字符串)并且不能升级或降级到它),您只能创建一个新类型,这仅仅意味着“编写一个新类”。至少在基本类型上,没有办法在没有别名的情况下继承行为。Anew_type<int>没有算术(除非你定义它)。
但你可以定义一个
template<class Innertype, class Tag>
class new_type
{
Innertype m;
public:
template<class... A>
explicit new_type(A&&... a) :m(std::forward<A>(a)...) {}
const Innertype& as_native() const { return m; }
};
Run Code Online (Sandbox Code Playgroud)
并且所有的锻炼只做一次。
template<class T, class I>
auto make_new_type(I&& i)
{ return new_type<I,T>(std::forward<I>(i)); }
template<class A, class B, class T>
auto operator+(const new_type<A,T>& a, const new_type<B,T>& b)
{ return make_new_type<T>(a.as_native()+b.as_native()); }
....
Run Code Online (Sandbox Code Playgroud)
进而
struct ID_tag;
typedef new_type<std::string,ID_tag> ID;
struct OtehrID_tag;
typedef new_type<std::string,OtehrID_tag> OtherID;
Run Code Online (Sandbox Code Playgroud)
和IDoandOtherID不能混合在表达式中。
NOTE:
Run Code Online (Sandbox Code Playgroud)
具有未指定返回值的 auto 函数是 C++14 中的标准函数,但 GCC 在 C++11 中也接受它。