2014-01-07 5 views
0

В C# можно проверить равенство для любого объекта против любого объекта, поскольку все наследуется от Object.Проверка равенства C++ 'Object'

В C++ это не тот случай. Я хотел бы перегрузить оператор ==, проходящий в любом неизвестном классе, и проверять во время выполнения на неравенство, а не на время компиляции. (Я получаю ошибку binary '!=' : no operator found which takes a left-hand operand of type).

Прямо сейчас я хочу это для целей TDD.

Как это сделать?

+0

Оставьте свой код :) – Drax

+6

Что вы подразумеваете под равенством? Есть несколько способов определить, что означает равенство, что ИМХО является одной из причин, по которым вы не можете сравнивать произвольные объекты в 'C++'. Некоторые параметры: один и тот же объект (т. Е. Адрес), побитовое, концептуально равное, ... –

+2

TDD на сильно типизированных языках обычно требует, чтобы вы определили, с какими типами вы работаете, когда пишете тест. –

ответ

4

В C# можно проверить равенство для любого объекта против какого-либо объекта, поскольку все наследуется от Object.

Это просто неверно. Хотя верно, что каждый класс наследует метод Equals, этот метод не обязательно делает ничего значимого, если он не был отменен.

Вы можете достичь того же в C++, но это довольно плохая практика, и, вероятно, не то, что вы хотите:

template <typename A, typename B> 
bool equals(A const& a, B const& b) { 
    return reinterpret_cast<char const*>(&a) == reinterpret_cast<char const*>(&b); 
} 

... это будет ваш резервный случай (реализации идентификатора ссылки). Теперь вы можете добавить одну, где типы реализации соответствующего оператора (через SFINAE):

template <typename T> 
auto equals(T const& a, T const& b) -> decltype(a == b) { 
    return a == b; 
} 

Однако, чтобы подтвердить, это не является ни советовал, ни вообще полезно. Это в основном бессмысленно - либо вы уже знаете, что (и как) вы можете сравнивать свои объекты, либо сравнение не имеет смысла для начала.

Дизайн C# явно плохой. Просто нет общей концепции «равенства», и тип C# Object в этом отношении слишком переопределен.


Для struct с, C# реализует почленно равенство, которое, возможно, более значимым, но также, как правило, не то, что хотел, и очень неэффективно для загрузки (так как он использует отражение).

+0

Это может быть неважно, но почему вы 'reinterpret_cast' в' char * '? Это пытается отбросить «const» ссылки. Мне также непонятно, почему вы не сравниваете 'void const *' вместо (через static/"implicit" cast). – dyp

+1

@ DyP Хорошая мысль о 'const' -ess. Причиной использования 'char *' является то, что 'char *' является (слабо названным) синонимом для 'byte *', то есть что-то логически приближается к адресу памяти. Я не знаю, что такое 'void *' (кроме стираемого типа). Я не использую его. –

+0

Каковы некоторые причины, по которым это не рекомендуется? – happygilmore

2

имитирующий C# механизм дает (помимо C# не имеет сопзЬ):

#include <iostream> 

class Object 
{ 
    public: 
    virtual ~Object() {} 
    virtual bool Equals(const Object& other) const { 
     // C# ReferenceEquals 
     return this == &other; 
    } 
}; 

bool operator == (const Object& a, const Object& b) { 
    std::cout << " Object == Object "; 
    // C# ReferenceEquals 
    return &a == &b; 
} 

class Derived : public Object 
{ 
    public: 
    virtual bool Equals(const Object& other) const { 
     // C# does a test if the object is null - not needed in C++ 
     const Derived* derived = dynamic_cast<const Derived*>(&other); 
     if(derived) return Equals(*derived); 
     else return false; 
    } 

    bool Equals(const Derived& other) const { 
     // Compare the members. 
     return true; 
    } 
}; 

// C# Guideline: Overriding operator == in non-immutable types is not recommended 
bool operator == (const Derived& a, const Derived& b) { 
    std::cout << "Derived == Derived "; 
    // C#: Test references conditions - not needed in C++: 
    //  if (System.Object.ReferenceEquals(a, b)) return true; 
    //  if (((object)a == null) || ((object)b == null)) return false; 
    return a.Equals(b); 
} 


// Test 

template <typename A, typename B> 
void compare(const char* what, const A& a, const B& b) { 
    std::cout << what << ": "; 
    std::cout << ((a == b) ? "True " : "False "); 
    std::cout << " Equals: " << ((a.Equals(b)) ? "True " : "False ") << '\n'; 
} 

int main() { 
    Derived a; 
    Derived b; 
    Object& c = a; 
    Object& d = b; 
    compare("a == b", a, b); 
    compare("a == c", a, c); 
    compare("e == d", c, d); 
} 

Выход:

a == b: Derived == Derived True Equals: True 
a == c: Object == Object True Equals: True 
e == d: Object == Object False Equals: True 

Я не рекомендую это в C++.

Примечания (от http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx)

Равно виртуальный метод, что позволяет любой класс, чтобы переопределить его реализации. Любой класс, который представляет значение, по существу, любой тип значения или набор значений в виде группы, например комплексное число , должен переопределять Equals. Если тип реализует IComparable, то должен переопределять Equals.

По умолчанию оператор == тесты на равенство ссылок по определяющим, если две ссылки указывают на один и тот же объект, так что ссылки типы не должны реализовывать оператор ==, чтобы получить эту функциональность. Когда тип неизменен, то есть данные, содержащиеся в , экземпляр не может быть изменен, оператор перегрузки == для сравнения равенство равенства вместо ссылочного равенства может быть полезным, поскольку, поскольку неизменяемых объектов, их можно считать одинаковыми до тех пор, пока они имеют значение . Переопределяющий оператор == в неизменяемых типах - не рекомендуется.

Смежные вопросы