Google 测试找不到用户提供的相等运算符

Google Test can't find user provided equality operator

我正在使用 Google 测试版 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 工作的所有模板,但忽略了我提供的模板。

我不明白的是为什么

有人可以对此有所启发吗?它一定是非常明显的东西,但我看不到它。

您的问题是由于 ADL(参数相关查找)引起的。正如您所知,std::vectorstd::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) );
}

简答:

未能找到正确的 operator== 模板主要是因为 Google 测试定义了它自己的 operator== 模板和不使用 ADL 选择的名称空间查找规则该模板并拒绝它。正如Amadeus指出的那样,ADL无法找到我定义的模板。

解决方法:

正如 Amadeus 指出的那样,不鼓励将内容移动到 std 命名空间(在本例中是为了让 ADL 工作)。

我对污染 Google Test 的命名空间没有任何疑虑,因此将我的模板移动到 ::testing::internals 解决了问题(使用普通查找,而不是 ADL)。

较长的答案:

我的期望是全局 operator== 应该通过 Vandevoorde & Josuttis, C++ 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) ;
}

编译成功。我对 Amadeus 的回答的解读是,Amadeus 认为应该因 ADL 而失败。但是,在这种情况下,ADL 是 而不是 用于查找 operator==。这可以通过重写

在对运算符的调用中显式关闭 ADL 来证明
expected == actual

作为

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

V&J 第 9.2.1 节,第 123 页:

ADL is also inhibited if the name of the function to be called is enclosed in parentheses

在这种情况下,代码仍然可以编译。

为了确定涉及 Google 测试的代码失败的原因,我对 Google 测试 headers 进行了相当极端的手术,直到我只提取了导致编译器错误的代码,这导致在 gtest/internal/gtest-linked_ptr.h:

testing::internal 命名空间中定义了 operator==
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, The C++ Programming Language, 4th Edition,第 26.3.5 页。 753 声明依赖名称的绑定是通过查看

完成的
  1. 范围内的名称在定义模板的位置加上
  2. 从属调用的参数的名称空间中的名称

在这种情况下,根据第一条规则,应该选择 A::operator== 而不是 ::operator==。 ADL 也无法找到 ::operator==,因为正如 Amadeus 指出的那样,它不在 std 命名空间中。

为了说服自己编译失败确实是第一条规则的结果,我将 ::operator== 的定义移到了 A 命名空间中,并像以前一样关闭了 ADL。

代码编译成功。