Why doesn't compiler recognize members of my type-parameterized test fixture class?

googletest

Solution

After pre-processing, your type-parameterized test:

TYPED_TEST_P(TypeParamFixture, FooTest) {
    N=6; /* COMPILE-ERROR: ‘N’ was not declared in this scope */
    ASSERT_LE(0,N);
}

turns into:

namespace gtest_case_TypeParamFixture_ {

template <typename gtest_TypeParam_> 
class FooTest : public TypeParamFixture<gtest_TypeParam_> 
{ 
    private: 
    typedef TypeParamFixture<gtest_TypeParam_> TestFixture; 
    typedef gtest_TypeParam_ TypeParam; virtual void TestBody(); 
}; 

static bool gtest_FooTest_defined_ __attribute__ ((unused)) = 
    gtest_typed_test_case_p_state_TypeParamFixture_.AddTestName(
        "gtest.cpp", 22, "TypeParamFixture", "FooTest");

} 

template <typename gtest_TypeParam_> 
void gtest_case_TypeParamFixture_::FooTest<gtest_TypeParam_>::TestBody()
{
    N=6; //<- Won't compile
    switch (0) case 0: default: 
        if (const ::testing::AssertionResult gtest_ar = 
            (::testing::internal::CmpHelperLE("0", "N", 0, N))) ; 
        else return ::testing::internal::AssertHelper(
                                                ::testing::TestPartResult::kFatalFailure, 
                                                "gtest.cpp", 
                                                24, 
                                                gtest_ar.failure_message()
                                            ) = ::testing::Message();
}

The key point about this is that your typed-parameterized test becomes a class template that inherits your type-parameterized test fixture.

In that light, the following very simple example illustrates the problem:

template<typename T>
struct my_base
{
    my_base() : N(0){}
    int N;
    int bar() const {
        return N; //<- This will compile
    }
};

template<typename T>
struct my_derived : my_base<T>
{
    int foo() {
        // return N; // <- This will not compile 
        return this->N; //<- This will compile
    }
};

int main()
{
    my_derived<int> d;
    return d.foo() + d.bar();
}

The lesson is that within the implementation of a class template derived from a class template, members of the base template must be referred to via the `this` pointer. That is just an inconvenient fact about the C++ template mechanism.

Problem

I'm writing a type-parameterized test fixture using gtest, and I'm seeing a `not declared in this scope` compiler error when I try to use members from the fixture class. When I'm not using a type-parameterized fixture, I can access class members just fine. When I am using a type-parameterized fixture, I can use `this->(member name)` to access the member. But why can't I use the member name explicitly? Is there a way I can avoid littering my test code with `this->` all over the place? Here's a trivial reproduction: Test_Sanity.h: ``` #include "gtest/gtest.h" /* Using a member in a simple, non-paramed fixture: */ class SimpleFixture : public ::testing::Test { protected: int N; }; TEST_F(SimpleFixture, FooTest) { N=6; ASSERT_LE(0,N); } /* Using a member in a type-parameterized fixture: */ template <typename PARAM_T> class TypeParamFixture : public ::testing::Test { protected: int N; }; TYPED_TEST_CASE_P(TypeParamFixture); TYPED_TEST_P(TypeParamFixture, FooTest) { N=6; /* COMPILE-ERROR: ‘N’ was not declared in this scope */ ASSERT_LE(0,N); } /* As above, but using this->N */ TYPED_TEST_P(TypeParamFixture, FooTestUsingThisPtr) { this->N=6; /* No compilation error */ ASSERT_LE(0,this->N); } /* Registration and instantiation of type-paramed tests */ REGISTER_TYPED_TEST_CASE_P(TypeParamFixture, FooTest, FooTestUsingThisPtr); struct StructA { int myInt; double myDouble; }; struct StructB { char myCharArr[42]; }; typedef ::testing::Types<StructA, StructB> MyTypes; INSTANTIATE_TYPED_TEST_CASE_P(Sanity, TypeParamFixture, MyTypes ); ``` I compile the above with the boilerplate gtest main, and get a compilation error only for the use of `N` in `TypeParamFixture/FooTest` .

Original source