区分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++ API:区分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<<(
// 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>;