Операторы для не примитивных коробочных типов

У меня есть следующее определение класса:

template <typename T>
class MyBox {
public:
MyBox(T value) { _value = value; }
operator T() const { return _value;  }
private:
T _value;
};

typedef MyBox<int> MyInt;
typedef MyBox<std::string> MyString;

Когда я пытаюсь использовать операторы на моих typedefs, как это

bool first = MyInt(1) == MyInt(1);    // works
bool second = std::string(MyString("a")) == std::string(MyString("a"));    //works
bool third = MyString("a") == MyString("a");   // does not compile

компилятор жалуется на третье сравнение

ни один оператор «==» не соответствует этим операндам. Типы операндов: MyString == MyString

и это происходит с любым другим не примитивным боксом (например, MyBox<float> работает но MyBox<std::map<int,int> > не. Почему это так?

Это особенно неясно для меня, потому что для первого и второго сравнения operator T() используется — почему это не может быть сделано автоматически для MyString также?

ОБНОВИТЬ: Есть ли простое решение для этого, кроме предоставления конкретных операторов для каждого не примитивного шаблона? И что делать с MyString("a") == std::string("a")?

3

Решение

Причины того, почему он работает для встроенных типов, но не работает для пользовательских типов, даны в следующем SO-вопросе: использование пользовательских преобразований с неявными преобразованиями в сравнениях. Короче говоря, это происходит потому, что преобразование типов не происходит для типов, выведенных из шаблона. И пока встроенный operator== за int не является шаблоном (и, следовательно, может быть найдено с помощью преобразования типа, когда MyBox<int> используется), operator== за std::string это шаблон.

Тем не менее, вопрос, упомянутый выше, не содержит подробностей о том, как решить эту проблему. Вот как: добавить следующие бесплатные функции

template<class T>
bool operator==(const MyBox<T>& lhs, const MyBox<T>& rhs) {
return static_cast<const T&>(lhs) == static_cast<const T&>(rhs);
}

template<class T>
bool operator==(const MyBox<T>& lhs, const T& rhs) {
return static_cast<const T&>(lhs) == rhs;
}

template<class T>
bool operator==(const T& lhs, const MyBox<T>& rhs) {
return lhs == static_cast<const T&>(rhs);
}
3

Другие решения

Ваш второй пример использует operator== из std::string, строки построены из std::string(const std::string&) (поскольку MyString::operator std::string () определено):

bool second = std::string(MyString("a")) == std::string(MyString("a")); // cals std::string::operator==()

Ваш третий пример не может работать: нет operator== это занимает два MyStrings:

bool third = MyString("a") == MyString("a");   // does not compile

Я не знаю почему, и я рассмотрю это, но есть специальное правило (стандарт или реализация), которое делает operator== можно найти для любого параметра шаблона POD MyBox:

#include <iostream>
#include <string>

template <typename T>
class MyBox {
public:
MyBox(T value) { _value = value; }
operator T() const { return _value;  }
private:
T _value;
};

struct S {};

int main()
{
MyBox<int> zero(0), one(1);
std::cout << (zero == one) << std::endl; // false

MyBox<void*> p1(&zero), p2(&one);
std::cout << (p1 == p2) << std::endl; // false

MyBox<S> s1(S{}), s2(S{});
std::cout << (s1 == s2) << std::endl; // error: no match for 'operator==' (operand types are 'MyBox<S>' and 'MyBox<S>')
}
-3