Не могу использовать перегруженный оператор сравнения с Catch test - PullRequest
2 голосов
/ 16 января 2020

У меня есть простой юнит-тест с использованием Catch 2.11.1:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <utility>
#include <any>

namespace A::B
{
    namespace C
    {
        struct S
        {
        };
    }

    using type = std::pair<C::S, std::any>;
}

inline bool operator==(A::B::type const&, A::B::type const&)
{
    return true;
}

TEST_CASE("test", "[test]")
{
    auto t1 = std::make_pair(A::B::C::S(), std::any());
    auto t2 = std::make_pair(A::B::C::S(), std::any());

    REQUIRE(t1 == t2);
}

Вышеуказанные простые программы генерируют следующие ошибки:

$ g++ -Wall -Wextra -Wpedantic test-single.cpp -std=c++17
In file included from /usr/include/c++/9/bits/stl_algobase.h:64,
                 from /usr/include/c++/9/bits/char_traits.h:39,
                 from /usr/include/c++/9/string:40,
                 from catch.hpp:457,
                 from test-single.cpp:2:
/usr/include/c++/9/bits/stl_pair.h: In instantiation of ‘constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&) [with _T1 = A::B::C::S; _T2 = std::any]’:
catch.hpp:2289:98:   required from ‘bool Catch::compareEqual(const LhsT&, const RhsT&) [with LhsT = std::pair<A::B::C::S, std::any>; RhsT = std::pair<A::B::C::S, std::any>]’
catch.hpp:2318:34:   required from ‘const Catch::BinaryExpr<LhsT, const RhsT&> Catch::ExprLhs<LhsT>::operator==(const RhsT&) [with RhsT = std::pair<A::B::C::S, std::any>; LhsT = const std::pair<A::B::C::S, std::any>&]’
test-single.cpp:28:5:   required from here
/usr/include/c++/9/bits/stl_pair.h:449:24: error: no match for ‘operator==’ (operand types are ‘const A::B::C::S’ and ‘const A::B::C::S’)
  449 |     { return __x.first == __y.first && __x.second == __y.second; }
      |              ~~~~~~~~~~^~~~~~~~~~~~

[И многие многие больше сообщений после этого ...]

Важной частью сообщения об ошибке является эта строка:

/usr/include/c++/9/bits/stl_pair.h: In instantiation of ‘constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&) [with _T1 = A::B::C::S; _T2 = std::any]’:

Из сообщения об ошибке ясно, что это стандарт std::operator== функция для std::pair, которая вызывается, вместо моей перегруженной функции operator==.

Если I не делает сравнение внутри макроса Catch REQUIRE, тогда он работает :

auto result = t1 == t2;  // Invokes my overloaded comparison operator
REQUIRE(result);

Теперь это проблема с Catch или с моей операторской функцией?


Примечание: я использую SID Debian с последней сборкой G CC 9,2

$ g++ --version
g++ (Debian 9.2.1-23) 9.2.1 20200110
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ответы [ 2 ]

5 голосов
/ 16 января 2020

Волхвы c, расширяющие операнды для обеспечения хорошей диагностики * Выходные данные 1015 * могут иногда падать.

Обходной путь - отключить это с помощью некоторых скобок:

REQUIRE((t1 == t2));

Это фактически тот же обходной путь, что и для переменной.

В документации эта проблема упоминается в контексте более сложных выражений . Точно, почему ситуация вызывается в вашем случае, я не уверен, но обратите внимание на трассировку стека, как ваш operator== на самом деле не вызывается, а вместо этого Catch::BinaryExpr::operator== и Catch::compareEqual, который, кажется, не имеет доступа к ( или иначе решит не использовать) вашу реализацию. В любом случае, решение состоит в том, чтобы отключить механизм разложения, как отмечено выше.

3 голосов
/ 16 января 2020

Обратите внимание, что даже с учетом скобок, предложенных Lightness, код, который вы показываете, является исключительно fr agile.

Я полагаю, что вы изначально находитесь на территории только ADL из-за поиска зависимых имен внутри макроса (см. последние примечания https://en.cppreference.com/w/cpp/language/adl), и ваш код явно не пригоден для ADL. Добавление скобок делает все это просто безусловным поиском, а не только ADL (опять же, предположение). Не-ADL часть неквалифицированного поиска спасет вас в этом случае, но она отделится от совершенно несвязанных изменений кода.

Рассмотрим этот код вместо TEST_CASE, то есть то, что использование скобок предположительно сводится к :

namespace test
{
    bool foo()
    {
        auto t1 = std::make_pair(A::B::C::S(), std::any());
        auto t2 = std::make_pair(A::B::C::S(), std::any());

        return t1 == t2;
    }
}

Это компилируется и работает, как и ожидалось: https://godbolt.org/z/HiuWWy

Теперь добавьте совершенно не связанный operator== между вашим глобальным operator== и t1 == t2:

namespace test
{
    struct X{};
    bool operator==(X, X);

    bool foo()
    {
        auto t1 = std::make_pair(A::B::C::S(), std::any());
        auto t2 = std::make_pair(A::B::C::S(), std::any());

        return t1 == t2;
    }
}

И вы рассчитываете на счет: https://godbolt.org/z/BUQC9Y

operator== в глобальном пространстве имен не найден, потому что ( не являющаяся частью ADL) поиск безусловного имени останавливается в включающей области действия first , которая имеет any operator==. Поскольку в этом нет ничего полезного, он возвращается к использованию встроенного оператора сравнения std::pair (найденного через ADL), который не будет работать.

Просто поместите перегрузки операторов в пространства имен объектов, которые они работать на. И, как следствие, не перегружайте операторов для объектов из std (или других пространств имен, к которым вы не имеете права прикасаться).


Добавление из комментариев:

Стандарт в настоящее время также говорит, что учитываются пространства имен аргументов шаблона, поэтому будет работать operator== в namespace C (поскольку отсюда первый аргумент шаблона std :: pair): https://godbolt.org/z/eV8Joj

Тем не менее, 1. это меня не устраивает sh не слишком хорошо с вашим псевдонимом типа, и 2. есть некоторое движение, чтобы сделать ADL менее диким, и я видел обсуждение, чтобы избавиться от "рассмотреть пространства имен параметров шаблона". ». См. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0934r0.pdf:

С какой стати мы будем изучать пространства имен аргументов шаблона? Ничто в нем не может быть частью интерфейса типа, если только аргументы шаблона не были также базовыми классами или чем-то еще. - Херб Саттер

Я не знаю, где эта статья стоит сегодня, но я бы не стал полагаться на этот вид ADL в новом коде.

...