Google Test не может найти предоставленного пользователем оператора равенства

Я использую Google Test v1.7

Я создал пользовательский operator ==, который ASSERT_EQ не может найти, но который можно найти, если он используется напрямую. Здесь код

#include <vector>
#include <deque>

#include "gtest/gtest.h"

template< typename T> struct bar { T b; };

template< typename T>
bool operator == ( const std::vector<T>& v, const bar<T>& b ) { return false; }

template< typename T>
bool operator==(  const std::vector<T>& v , const std::deque<T>& d) { return false; }

TEST( A, B ) {

    std::vector<char> vec;
    std::deque<char> deq;
    bar<char> b;

    // compiles
    ASSERT_EQ( vec, b );

    // compiles
    vec == deq;

    // doesn't compile
    ASSERT_EQ( vec, deq );
}

В строке ASSERT_EQ( vec, deq ) появляется следующее сообщение от Apple 6.0 clang:

test/gtest.h:18861:16: error: invalid operands to binary expression ('const std::__1::vector<char, std::__1::allocator<char> >' and 'const
      std::__1::deque<char, std::__1::allocator<char> >')
  if (expected == actual) {
      ~~~~~~~~ ^  ~~~~~~
../x86_64-linux_debian-7/tests/gtest/gtest.h:18897:12: note: in instantiation of function template specialization 'testing::internal::CmpHelperEQ<std::__1::vector<char,
      std::__1::allocator<char> >, std::__1::deque<char, std::__1::allocator<char> > >' requested here
    return CmpHelperEQ(expected_expression, actual_expression, expected,
           ^
tst.cc:27:5: note: in instantiation of function template specialization 'testing::internal::EqHelper<false>::Compare<std::__1::vector<char, std::__1::allocator<char> >,
      std::__1::deque<char, std::__1::allocator<char> > >' requested here
        ASSERT_EQ( vec, deq );
        ^

в то время как gcc 4.7.2 перечисляет все шаблоны, которые он пытался, и не смог выполнить expected == actual, игнорируя тот, который я предоставил.

Я не понимаю, почему

  • ASSERT_EQ( vec, b ) находит предоставленный operator ==; и
  • vec == deq компилируется; но
  • ASSERT_EQ( vec, deq ) нет.

Может ли кто-нибудь пролить свет на это? Это должно быть что-то невероятно очевидное, но я не вижу этого.

Ответ 1

Короткий ответ:

Не удается найти правильный шаблон operator==, в первую очередь, из-за того, что Google Test определяет собственный шаблон operator==, а правило поиска пространства имен, которое не использует ADL, выбирает этот шаблон и отклоняет его. ADL, как указал Amadeus, не может надеяться найти шаблон, который я определил.

Решение:

Как указывает Amadeus, перемещение объектов в пространство имен std (в данном случае для работы ADL) не рекомендуется.

У меня нет проблем с загрязнением пространства имен Google Test, поэтому перемещение моего шаблона в ::testing::internals решает проблему (используя обычный поиск, а не ADL).

Более длинный ответ:

Я ожидал, что глобальный operator== должен быть обнаружен через Vandevoorde и Josuttis, С++ Templates. Раздел 9.2, p. 122, термин обычный поиск.

Вот код, иллюстрирующий это:

#include <vector>
#include <deque>

template< typename T>
bool operator==(  const std::vector<T>& v , const std::deque<T>& d);

namespace A {

    template <typename T1, typename T2>
    bool EQ( const T1& expected, const T2& actual ) {
        return expected == actual;
    }
}

void TestBody() {

    std::vector<char> vec;
    std::deque<char> deq;

    ::A::EQ(vec, deq) ;
}

Это успешно скомпилируется. Мое чтение ответа Амадеуса заключается в том, что Амадеус полагает, что он должен потерпеть неудачу из-за ADL. Однако в этом случае ADL не используется для поиска operator==. Это можно продемонстрировать, явно отключив ADL при вызове оператора, переписав

expected == actual

а

return (operator==)( expected, actual);

V & J Раздел 9.2.1, стр .123:

ADL также запрещен, если имя вызываемой функции заключено в круглые скобки

и в этом случае код все еще компилируется.

Чтобы определить, почему код, связанный с Google Test, не удался, я сделал довольно экстремальную операцию в заголовках Google Test, пока не извлек только код, вызывающий ошибки компилятора, что привело к определению operator== в testing::internal пространство имен в gtest/internal/gtest-linked_ptr.h:

namespace testing {
namespace internal {
[...]
template<typename T> inline
bool operator==(T* ptr, const linked_ptr<T>& x) {
  return ptr == x.get();
}
[...]
}
}

Перевод этого кода в мой тестовый код приводит к:

#include <vector>
#include <deque>

template< typename T>
bool operator==(  const std::vector<T>& v , const std::deque<T>& d);

namespace A {

    struct S {};
    template<typename T> bool operator==(T* ptr, S& x);

    template <typename T1, typename T2>
    bool EQ( const T1& expected, const T2& actual ) {
      return expected == actual;
    }
}

void TestBody() {

    std::vector<char> vec;
    std::deque<char> deq;

    ::A::EQ(vec, deq) ;
}

Это успешно не удается скомпилировать с ошибками неправильного шаблона. Представляет интерес первое сообщение об ошибке:

gtst.cc: In instantiation of ‘bool A::EQ(const T1&, const T2&) [with T1 = std::vector<char>; T2 = std::deque<char>]’:
gtst.cc:25:21:   required from here
gtst.cc:14:37: error: no matching function for call to ‘operator==(const std::vector<char>&, const std::deque<char>&)’
gtst.cc:14:37: note: candidates are:
gtst.cc:10:31: note: template<class T> bool A::operator==(T*, A::S&)
gtst.cc:10:31: note:   template argument deduction/substitution failed:
gtst.cc:14:37: note:   mismatched types ‘T*’ and ‘std::vector<char>’

Итак, он сначала смотрит на A::operator==.

Stroustrup, язык программирования С++, 4-е издание, раздел 26.3.5 с. 753 говорится, что привязка зависимых имен выполняется путем просмотра

  • Имена в области в точке, где указан шаблон, плюс
  • имена в пространстве имен аргумента зависимого вызова

В этом случае по первому правилу следует выбрать A::operator==, а не ::operator==. ADL также не находит ::operator==, потому что, как указывает Amadeus, он не находится в пространстве имен std.

Чтобы убедиться, что сбой компиляции действительно является результатом первого правила, я переместил определение ::operator== в пространство имен A и выключил ADL, как и раньше.

Код успешно компилируется.

Ответ 2

Ваша проблема связана с ADL (зависимым от аргумента поиска). std::vector и std::deque, как вы уже знаете, определены в пространстве имен std, но вы определяете operator== в глобальном пространстве имен, и ADL не находит эту функцию.

Чтобы решить вашу проблему, вы должны определить operator== в том же пространстве имен ваших контейнеров, которое находится внутри пространства имен std. Проблема в том, что для этого вы НЕ ДОПУСКАЕТ. Таким образом, я бы предложил вам немного изменить свой подход. Почему бы не попробовать что-то вроде этого:

template< typename T>
bool equal( const std::vector<T>& v , const std::deque<T>& d) { return false; }

TEST( A, B ) {
    std::vector<char> vec;
    std::deque<char> deq;
    ASSERT_TRUE( equal(vec, deq) );
}