В нашем проекте мы используем довольно много "привычек", чтобы явно указать, что должна представлять переменная. Он в основном используется для идентификаторов std::string
, таких как PortalId
или CakeId
. Теперь мы можем сделать
using PortalId = std::string;
using CakeId = std::string;
PortalId portal_id("2");
CakeId cake_id("is a lie");
portal_id = cake_id; // OK
что нам не нравится. Мы хотели бы иметь проверку типа во время компиляции, чтобы мы не смешивали яблоки и апельсины, сохраняя при этом большинство методов yum yum из исходного объекта.
Итак, вопрос в том, может ли это быть сделано на С++, так что использование будет близким к следующему, присваивания потерпят неудачу, и мы все еще можем использовать его в картах и других материалах?
SAFE_TYPEDEF(std::string, PortalId);
SAFE_TYPEDEF(std::string, CakeId);
int main()
{
PortalId portal_id("2");
CakeId cake_id("is a lie");
std::map<CakeId, PortalId> p_to_cake; // OK
p_to_cake[cake_id] = portal_id; // OK
p_to_cake[portal_id] = cake_id; // COMPILER ERROR
portal_id = cake_id; // COMPILER ERROR
portal_id = "1.0"; // COMPILER ERROR
portal_id = PortalId("42"); // OK
return 0;
}
Мы уже пробовали некоторые макросы в сочетании с шаблонами, но не получили то, что нам было нужно. И добавить - мы можем использовать С++ 14.
EDIT: Код, с которым мы столкнулись, был
#define SAFE_TYPEDEF(Base, name) \
class name : public Base { \
public: \
template <class... Args> \
explicit name (Args... args) : Base(args...) {} \
const Base& raw() const { return *this; } \
};
который является уродливым и не работает. И этим не работает, я имею в виду, что компилятор был в порядке с .portal_id = cake_id;
EDIT2: Добавлено ключевое слово explicit
, с помощью которого наш код действительно хорошо работает для примера. Не уверен, хотя это правильный путь, и охватывает ли он все неудачные ситуации.