区分typedef 我正在为C库编写C++抽象。C库中有几个用于标识远程资源的ID的typedef: typedef int color_id; typedef int smell_id; typedef int flavor_id; // ... color_id createColor( connection* ); void destroyColor( connection*, color_id ); // ...

区分typedef 我正在为C库编写C++抽象。C库中有几个用于标识远程资源的ID的typedef: typedef int color_id; typedef int smell_id; typedef int flavor_id; // ... color_id createColor( connection* ); void destroyColor( connection*, color_id ); // ...,c++,types,overloading,typedef,C++,Types,Overloading,Typedef,当然,在编译器眼中,所有这些typedef都是相同的类型。这对我来说是个问题,因为我想重载函数和专门化模板来提供一个友好的C++ API: // can't do the following since `color_id`, `smell_id` and `int` are the same std::ostream& operator<<( std::ostream&, color_id ); std::ostream& operator<<(

当然,在编译器眼中,所有这些typedef都是相同的类型。这对我来说是个问题,因为我想重载函数和专门化模板来提供一个友好的C++ API:

// can't do the following since `color_id`, `smell_id` and `int` are the same
std::ostream& operator<<( std::ostream&, color_id );
std::ostream& operator<<( std::ostream&, smell_id );
void destroy( connection*, color_id );
void destroy( connection*, smell_id );

// no static check can prevent the following
smell_id smell = createSmell( connection );
destroyColor( connection, smell ); // it's a smell, not a color!
//无法执行以下操作,因为'color\u id'、'some\u id'和'int'是相同的
std::ostream和operatorOne包装类来管理它们
您最好的选择是创建一个包装类,但是使用模板,我们可以编写一个包装类模板,并将其用于所有不同的ID,只需将它们分配给模板的不同实例即可

template<class ID> 
struct ID_wrapper
{
    constexpr static auto name() -> decltype(ID::name()) {
        return ID::name(); 
    }
    int value;

    // Implicitly convertible to `int`, for C operability
    operator int() const {
        return value; 
    }  
};
打印带有名称的ID

C++,C++也可以强制你编写大量的样板代码来做“C++方式”。真糟糕,不是吗?只要问问自己编译时的安全性是否值得。为什么不为每个

color\u id
sense\u id
等创建一个类呢?您可以检查一些处理此类问题的现有OpenGL包装器。使用
enum class:int
template<class ID>
class std::hash<ID_wrapper<ID>> : public std::hash<int>
{
   public:
    // I prefer using Base to typing out the actual base
    using Base = std::hash<int>;

    // Provide argument_type and result_type
    using argument_type = int;
    using result_type = std::size_t; 

    // Use the base class's constructor and function call operator
    using Base::Base; 
    using Base::operator(); 
};
template<class ID>
std::ostream& operator<<(std::ostream& stream, ID_Wrapper<ID> id) {
    stream << '(' << ID_Wrapper<ID>::name() << ": " << id.value << ')'; 
    return stream; 
}
struct ColorIDTraits {
    constexpr static const char* name() {
        return "color_id";
    }
};

struct SmellIDTraits {
    constexpr static const char* name() {
        return "smell_id";
    }
};

struct FlavorIDTraits {
    constexpr static const char* name() {
        return "flavor_id";
    }
};
using color_id = ID_wrapper<ColorIDTraits>;
using smell_id = ID_wrapper<SmellIDTraits>;
using flavor_id = ID_wrapper<FlavorIDTraits>;