GTest 的 EXPECT_EQ 给出未定义的引用错误
GTest's EXPECT_EQ giving undefined reference to error
#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> ap2
、ArrayParam<5> ap3
等编写单个测试。
我得到的错误:
~/tests.cpp.o: 在函数 ParamTest_SizeTest_Test::TestBody()': ~/tests.cpp: undefined reference to
ArrayParam<3>::_length' collect2: error: ld returned 1 exit status
说明
通常,C++ 中的静态数据成员需要在 class 之外定义,例如:
struct A {
static int myInt;
};
A::myInt; //doesn't even have to be initialized
Const和non-volatile成员比较特殊,可见in the reference。它们可以用 class 主体内的任何常量表达式初始化:
struct A {
static const int myInt = 1;
};
int main() {
std::cout << A::myInt;
}
但是,这条规则有一个例外(来自 cppreference 中的同一段,强调我的):
If a const [non-inline (since C++17)] static data member [or a constexpr
static data member (since C++11)] is odr-used, a definition at
namespace scope is still required, but it cannot have an initializer.
This definition is deprecated for constexpr data members (since
C++17).
odr-used 是这样解释的(强调我的):
Informally, an object is odr-used if its value is read (unless it is a
compile time constant) or written, its address is taken, or a
reference is bound to it; a reference is odr-used if it is used and
its referent is not known at compile time; and a function is odr-used
if a function call to it is made or its address is taken. If an
object, a reference or a function is odr-used, its definition must
exist somewhere in the program; a violation of that is usually a
link-time error.
这正是这里发生的事情。 EXPECT_EQ
通过 const T&
获取参数,即 将引用 绑定到此类型。由于引用绑定到 _length
,它使它成为 odr-used
并且需要一个超出 class 的成员定义。
odr-used
异常不适用于 printf
,因为 printf
(作为 C 函数)不引用。它属于定义的 "read (unless it is a compile time constant)" 部分。由于我们有一个编译时间常量,所以一切正常。
解决方案
如果您使用的是 C++17,只需将 const
更改为 constexpr
即可:
template<int N>
struct ArrayParam<N> {
static constexpr int _length = N;
int _arr[N];
};
C++17 标准为命名空间范围内的 constexpr static
成员定义 已弃用(你不仅不必使用它,实际上你不应该使用它)。
如果您不使用 C++17,则必须在与 class:
相同的命名空间中添加此数据成员的定义
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++ 程序员识别。
作为大多数标准容器,它是高效的,并且由经验丰富的人编写得很好。在您之前使用它的无数程序员已经过测试并证明它很好。
#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> ap2
、ArrayParam<5> ap3
等编写单个测试。
我得到的错误:
~/tests.cpp.o: 在函数 ParamTest_SizeTest_Test::TestBody()': ~/tests.cpp: undefined reference to
ArrayParam<3>::_length' collect2: error: ld returned 1 exit status
说明
通常,C++ 中的静态数据成员需要在 class 之外定义,例如:
struct A {
static int myInt;
};
A::myInt; //doesn't even have to be initialized
Const和non-volatile成员比较特殊,可见in the reference。它们可以用 class 主体内的任何常量表达式初始化:
struct A {
static const int myInt = 1;
};
int main() {
std::cout << A::myInt;
}
但是,这条规则有一个例外(来自 cppreference 中的同一段,强调我的):
If a const [non-inline (since C++17)] static data member [or a constexpr static data member (since C++11)] is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. This definition is deprecated for constexpr data members (since C++17).
odr-used 是这样解释的(强调我的):
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; a reference is odr-used if it is used and its referent is not known at compile time; and a function is odr-used if a function call to it is made or its address is taken. If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error.
这正是这里发生的事情。 EXPECT_EQ
通过 const T&
获取参数,即 将引用 绑定到此类型。由于引用绑定到 _length
,它使它成为 odr-used
并且需要一个超出 class 的成员定义。
odr-used
异常不适用于 printf
,因为 printf
(作为 C 函数)不引用。它属于定义的 "read (unless it is a compile time constant)" 部分。由于我们有一个编译时间常量,所以一切正常。
解决方案
如果您使用的是 C++17,只需将 const
更改为 constexpr
即可:
template<int N>
struct ArrayParam<N> {
static constexpr int _length = N;
int _arr[N];
};
C++17 标准为命名空间范围内的 constexpr static
成员定义 已弃用(你不仅不必使用它,实际上你不应该使用它)。
如果您不使用 C++17,则必须在与 class:
相同的命名空间中添加此数据成员的定义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++ 程序员识别。
作为大多数标准容器,它是高效的,并且由经验丰富的人编写得很好。在您之前使用它的无数程序员已经过测试并证明它很好。