具有更高意义的简单类型(C++ 11)

DrP*_*p3r 5 c++ boost c++11

我经常遇到这种情况(在我的C++/C++ 11代码中),其中我的类型基本上像内置类型(或类似的"基本简单"类型std::string),但其含义超过32位号或一堆字符.我没有在互联网上找到任何有用的东西,因为我真的没有什么条件可以搜索...

例子:

  • 我曾经在一个系统上工作,其中的项目是通过ID识别的.这些ID是std::strings(首先可能不是最好的主意,但这是一个不同的故事).但真正糟糕的是,这些ID以std::strings或const char*s的形式通过系统传递.因此,在搜索类型时,很难(不可能)判断代码库ID中的位置.变量名称是ID(ID,id,Id)或密钥的全部变体,或者只是i或名称或其他.所以你也不能按名字搜索.所以我更愿意将这些变量作为类型传递id_t.
  • 网络端口:它们是uint16_ts.但我想把它们作为network_port_ts 传递给我们.

我通常使用typedef使事情变得更好.这种方法有许多问题:

  • 您不必使用typedef.您仍然可以通过"原始"类型传递变量(例如,std::string而不是id_t).
  • 如果原始类型是模板,则完成前向声明typedef(例如,使用shared_ptr).
  • "转发声明"typedef是一个维护问题.如果原始类型发生变化,您可以在整个地方更改内容.

我尝试使用网络端口示例的另一件事是编写一个瘦的包装类运动operator uint16_t.这解决了前向声明的问题.但后来我遇到了一些陷阱,其中包含一些内部使用printf的日志记录宏.printfs仍然工作(好,编译),但没有打印端口号,但(我认为)对象的地址.

我认为尺寸像重量或长度Boost.Units可能值得一看(即使它看起来有点"沉重").但是对于上面的两个例子,它并不合适.

实现我想要的最佳实践是什么(使用Boost是一种选择)?

简而言之: 我想要实现的是将"具有更高意义的类型"作为自己的类型而不是普通的原始/低级/非抽象类型.(有点像)具有用户定义的类型.优选地,没有为具有基本相同的实现的每种类型编写完整类的巨大开销,仅仅能够执行已经可以执行的内置操作.

seh*_*ehe 6

1.强大的Typedef

您可以使用BOOST_STRONG_TYPEDEF来获得一些便利.

确实使用宏,我认为你可以进行异构比较(例如id == "123").

有两个版本,一定要从Boost Utility中选择一个版本.

2. flavoured_string <>

对于字符串,您可以使用调味字符串欺骗系统(发明人: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)


Emi*_*lia 2

要创建一个不是整数的整数(或者不是字符串的字符串)并且不能升级或降级到它),您只能创建一个新类型,这仅仅意味着“编写一个新类”。至少在基本类型上,没有办法在没有别名的情况下继承行为。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 中也接受它。