floating-point numbers), the conversion from `T` to `U` is not lossy (in
other words, any value representable by `T` can also be represented by `U`);
and
-3. When `U` is a reference, `T` must also be a reference (as the underlying
- matcher may be interested in the address of the `U` value).
+3. When `U` is a non-const reference, `T` must also be a reference (as the
+ underlying matcher may be interested in the address of the `U` value).
The code won't compile if any of these conditions isn't met.
}
private:
- class Impl : public MatcherInterface<T> {
+ // If it's possible to implicitly convert a `const T&` to U, then `Impl` can
+ // take that as input to avoid a copy. Otherwise, such as when `T` is a
+ // non-const reference type or a type explicitly constructible only from a
+ // non-const reference, then `Impl` must use `T` as-is (potentially copying).
+ using ImplArgT =
+ typename std::conditional<std::is_convertible<const T&, const U&>::value,
+ const T&, T>::type;
+
+ class Impl : public MatcherInterface<ImplArgT> {
public:
explicit Impl(const Matcher<U>& source_matcher)
: source_matcher_(source_matcher) {}
// We delegate the matching logic to the source matcher.
- bool MatchAndExplain(T x, MatchResultListener* listener) const override {
+ bool MatchAndExplain(ImplArgT x,
+ MatchResultListener* listener) const override {
using FromType = typename std::remove_cv<typename std::remove_pointer<
typename std::remove_reference<T>::type>::type>::type;
using ToType = typename std::remove_cv<typename std::remove_pointer<
// Do the cast to `U` explicitly if necessary.
// Otherwise, let implicit conversions do the trick.
- using CastType =
- typename std::conditional<std::is_convertible<T&, const U&>::value,
- T&, U>::type;
+ using CastType = typename std::conditional<
+ std::is_convertible<ImplArgT&, const U&>::value, ImplArgT&, U>::type;
return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
listener);
// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
// contravariant): just keep a copy of the original Matcher<U>, convert the
// argument from type T to U, and then pass it to the underlying Matcher<U>.
-// The only exception is when U is a reference and T is not, as the
+// The only exception is when U is a non-const reference and T is not, as the
// underlying Matcher<U> may be interested in the argument's address, which
-// is not preserved in the conversion from T to U.
+// cannot be preserved in the conversion from T to U (since a copy of the input
+// T argument would be required to provide a non-const reference U).
template <typename T, typename U>
inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
// Enforce that T can be implicitly converted to U.
static_assert(std::is_convertible<const T&, const U&>::value,
- "T must be implicitly convertible to U");
- // Enforce that we are not converting a non-reference type T to a reference
- // type U.
- static_assert(std::is_reference<T>::value || !std::is_reference<U>::value,
- "cannot convert non reference arg to reference");
+ "T must be implicitly convertible to U (and T must be a "
+ "non-const reference if U is a non-const reference)");
// In case both T and U are arithmetic types, enforce that the
// conversion is not lossy.
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
int value_;
};
+// For testing casting matchers between compatible types. This is similar to
+// IntValue, but takes a non-const reference to the value, showing MatcherCast
+// works with such types (and doesn't, for example, use a const ref internally).
+class MutableIntView {
+ public:
+ // An int& can be statically (although not implicitly) cast to a
+ // MutableIntView.
+ explicit MutableIntView(int& a_value) : value_(a_value) {}
+
+ int& value() const { return value_; }
+
+ private:
+ int& value_;
+};
+
// For testing casting matchers between compatible types.
bool IsPositiveIntValue(const IntValue& foo) { return foo.value() > 0; }
+// For testing casting matchers between compatible types.
+bool IsPositiveMutableIntView(MutableIntView foo) { return foo.value() > 0; }
+
// Tests that MatcherCast<T>(m) works when m is a Matcher<U> where T
// can be statically converted to U.
TEST(MatcherCastTest, FromCompatibleType) {
// predicate.
EXPECT_TRUE(m4.Matches(1));
EXPECT_FALSE(m4.Matches(0));
+
+ Matcher<MutableIntView> m5 = Truly(IsPositiveMutableIntView);
+ Matcher<int> m6 = MatcherCast<int>(m5);
+ // In the following, the arguments 1 and 0 are statically converted to
+ // MutableIntView objects, and then tested by the IsPositiveMutableIntView()
+ // predicate.
+ EXPECT_TRUE(m6.Matches(1));
+ EXPECT_FALSE(m6.Matches(0));
}
// Tests that MatcherCast<T>(m) works when m is a Matcher<const T&>.
TEST(MatcherCastTest, FromConstReferenceToNonReference) {
- Matcher<const int&> m1 = Eq(0);
+ int n = 0;
+ Matcher<const int&> m1 = Ref(n);
Matcher<int> m2 = MatcherCast<int>(m1);
- EXPECT_TRUE(m2.Matches(0));
- EXPECT_FALSE(m2.Matches(1));
+ int n1 = 0;
+ EXPECT_TRUE(m2.Matches(n));
+ EXPECT_FALSE(m2.Matches(n1));
+}
+
+// Tests that MatcherCast<T&>(m) works when m is a Matcher<const T&>.
+TEST(MatcherCastTest, FromConstReferenceToReference) {
+ int n = 0;
+ Matcher<const int&> m1 = Ref(n);
+ Matcher<int&> m2 = MatcherCast<int&>(m1);
+ int n1 = 0;
+ EXPECT_TRUE(m2.Matches(n));
+ EXPECT_FALSE(m2.Matches(n1));
}
// Tests that MatcherCast<T>(m) works when m is a Matcher<T&>.
Matcher<int> m2 = MatcherCast<int>(m1);
EXPECT_TRUE(m2.Matches(0));
EXPECT_FALSE(m2.Matches(1));
+
+ // Of course, reference identity isn't preserved since a copy is required.
+ int n = 0;
+ Matcher<int&> m3 = Ref(n);
+ Matcher<int> m4 = MatcherCast<int>(m3);
+ EXPECT_FALSE(m4.Matches(n));
}
// Tests that MatcherCast<const T&>(m) works when m is a Matcher<T>.
EXPECT_FALSE(m4.Matches(d2));
}
+// Tests that SafeMatcherCast<T>(m) works when m is a Matcher<const T&>.
+TEST(SafeMatcherCastTest, FromConstReferenceToNonReference) {
+ int n = 0;
+ Matcher<const int&> m1 = Ref(n);
+ Matcher<int> m2 = SafeMatcherCast<int>(m1);
+ int n1 = 0;
+ EXPECT_TRUE(m2.Matches(n));
+ EXPECT_FALSE(m2.Matches(n1));
+}
+
// Tests that SafeMatcherCast<T&>(m) works when m is a Matcher<const T&>.
TEST(SafeMatcherCastTest, FromConstReferenceToReference) {
int n = 0;
// This file tests some commonly used argument matchers.
#include <array>
+#include <cstdint>
#include <memory>
#include <ostream>
#include <string>
EXPECT_FALSE(m.Matches(empty));
}
+TYPED_TEST(OptionalTest, ComposesWithMonomorphicMatchersTakingReferences) {
+ const Matcher<const int&> eq1 = Eq(1);
+ const Matcher<const int&> eq2 = Eq(2);
+ TypeParam opt(1);
+ EXPECT_THAT(opt, Optional(eq1));
+ EXPECT_THAT(opt, Optional(Not(eq2)));
+ EXPECT_THAT(opt, Optional(AllOf(eq1, Not(eq2))));
+}
+
+TYPED_TEST(OptionalTest, ComposesWithMonomorphicMatchersRequiringConversion) {
+ const Matcher<int64_t> eq1 = Eq(1);
+ const Matcher<int64_t> eq2 = Eq(2);
+ TypeParam opt(1);
+ EXPECT_THAT(opt, Optional(eq1));
+ EXPECT_THAT(opt, Optional(Not(eq2)));
+ EXPECT_THAT(opt, Optional(AllOf(eq1, Not(eq2))));
+}
+
template <typename T>
class MoveOnlyOptionalTest : public testing::Test {};