GTest的EXPECT_EQ给出了错误的未定义引用

问题描述 投票:0回答:1
#include <gtest/gtest.h>

template<typename T, size_t N>
size_t getSize(T (&arr)[N]){
 return N; 
}

template<int N>
struct ArrayParam {
  static const int _length = N;
  int _arr[N];
};

ArrayParam<3> ap1 = {{1,2,3}};
//ArrayParam<4> ap2 = {{1,2,3,4}};

class ParamTest: public ::testing::TestWithParam<ArrayParam<3>>{};

TEST_P(ParamTest, SizeTest){
  ArrayParam<3> param = GetParam();

  printf("\nparam._length == %d\n",param._length); //OK
  printf("\nValue2 == %d\n",ArrayParam<3>::_length); //OK

  //EXPECT_EQ(param._length,getSize(param._arr)); //NOT OK
  //EXPECT_EQ(3,param._length); // NOT OK
  EXPECT_EQ(3,sizeof(param._arr)/sizeof(int)); //OK

}

INSTANTIATE_TEST_CASE_P(ArraySize,ParamTest,testing::Values(ap1));

当我尝试访问_length时,有人知道为什么EXPECT_EQ不能用作printf吗?

我的最终目标是为各种ArrayParam<T>实例对象编写单个测试,例如ArrayParam<4> ap2ArrayParam<5> ap3等。

我得到的错误:

〜/ tests.cpp.o:在函数ParamTest_SizeTest_Test::TestBody()': ~/tests.cpp: undefined reference toArrayParam <3> :: _ length'collect2:error:ld返回1退出状态

c++ googletest
1个回答
2
投票

说明

通常,C ++中的静态数据成员需要在类之外进行定义,如下所示:

struct A {
static int myInt;
};
A::myInt; //doesn't even have to be initialized

Const和非易失性成员是特殊的,可以看出in the reference。它们可以使用类体内的任何常量表达式进行初始化:

struct A {
static const int myInt = 1;
};

int main() {
    std::cout << A::myInt;
}

但是,这条规则有一个例外(来自cppreference中的同一段,强调我的):

如果const [非内联(自C ++ 17)]静态数据成员[或constexpr静态数据成员(自C ++ 11)]为odr-used,则仍需要命名空间作用域的定义,但它不能有初始化。 constexpr数据成员不推荐使用此定义(自C ++ 17起)。

odr-used被解释为(强调我的):

非正式地,如果一个对象的值被读取(除非它是一个编译时常量)或者被写入,它的地址被采用,或者一个引用被绑定到它上面,它就被使用了。如果使用引用并且在编译时不知道它的引用,则引用是odr-used;如果对函数进行了函数调用或者对其进行了地址处理,则函数会被使用。如果一个对象,一个引用或一个函数被使用,它的定义必须存在于程序的某个地方;违反此通常是链接时错误。

这正是这里发生的事情。 EXPECT_EQ通过const T&获取参数,即绑定对此类型的引用。并且由于引用绑定到_length,它使它成为odr-used并且需要一个类外成员定义。

odr-used例外不适用于printf,因为printf(作为C函数)不参考。它属于“读取(除非它是编译时常量)”定义的一部分。由于我们有一个编译时间常数,一切正常。

如果您使用的是C ++ 17,那就像将const更改为constexpr一样简单:

template<int N>
struct ArrayParam<N> {
  static constexpr int _length = N; 
  int _arr[N];
};

C ++ 17标准为命名空间范围内的constexpr static成员定义了(不仅你不必使用它,你实际上不应该使用它)。


如果您不使用C ++ 17,则必须在与该类相同的命名空间中添加此数据成员的定义:

template<int N>
struct ArrayParam<N> {
    static constexpr int _length = N; //const is fine as well
    int _arr[N];
 };
template<int N>
constexpr int ArrayParam<N>::_length;

这将允许您将其与GoogleTest的EXPECT_EQ一起使用


作为旁注,我建议再一次使用std::array。它更易读,易于被所有C ++程序员识别。 作为大多数标准容器,它是高效的,并且由具有丰富经验的人员编写。并且经过测试并证明无数程序员在您之前使用它。

© www.soinside.com 2019 - 2024. All rights reserved.