I defined a constructor of a class as below:
struct TestClass
{
TestClass(std::uint8_t, std::vector<Type>)
{
//...
}
};
Then I want to use google test to do a unit test, the compile error occurs when I try to construct an instance of TestClass, as below:
TEST(name1,name2)
{
//...
Type element;
std::vector<Type> lst{element};
TestClass instance(0, lst);
//...
}
The gnu compiler reports:
"In function name1_name2_Test::TestBody()':test.cpp:(.text+0x165d): undefined reference toTestClass::TestClass(signed char, std::vector< Type, std::allocator< Type>)'
collect2: error: ld returned 1 exit status"
Below is my evironment:
g++ version: 5.3.0
google test version: 1.7.0
the compile command: g++ -D_GLIBCXX_USE_CXX11_ABI=0 test.cpp -std=c++14 -lboost_system -lgtest -lgtest_main -lpthread
Have you ever met similar problem? Please give me some suggestions, thanks.
Below code can cause above error:
#include <gtest/gtest.h>
struct Element
{
Element(const std::vector<std::uint8_t>& element) : element_(element){}
std::vector<std::uint8_t> element_;
};
using ElementList = std::vector<Element>;
struct Information
{
Information(std::uint8_t, ElementList) {}
std::int8_t number_;
ElementList eleLst_;
};
TEST(name1,name2)
{
std::uint8_t number = 0;
std::vector<std::uint8_t> vec{1,2,3,4};
Element elem(vec);
ElementList lst{elem};
Information info(std::int8_t(0), lst); //false
}
GTEST_API_ int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Below is compile message:
/tmp/ccUzsTfz.o: In function name1_name2_Test::TestBody()':
test.cpp:(.text+0x130): undefined reference toInformation::Information(unsigned char, std::vector< Element, std::allocator< Element > >)'
collect2: error: ld returned 1 exit status
Your signature is:
struct TestClass
{
TestClass(std::uint8_t, std::vector<Type>)
{
//...
}
};
And the LINKER looks for:
TestClass::TestClass(signed char, std::vector< Type>, std::allocator< Type>);
Your TestClass' constructor definition does not contain an allocator-parameter!
That's why there's no function instantiated containing the allocator-parameter and consequently it is not found by the linker.
EDIT: Some more reference
http://www.cplusplus.com/doc/tutorial/classes/ // Basics
https://isocpp.org/wiki/faq/ctors // Good to know
EDIT: There was some misunderstanding
TestClass(signed char, std::vector<Type>)
as the constructor is enough, if you use a standard allocator of type "Type"..
See:
http://de.cppreference.com/w/cpp/container/vector
THis is the signature of the class:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
As you can see, it has a default template parameter assignment for Allocator, which is std::allocator.
This way, you only have to specify an allocator In the template parameter list, if you have a custom allocator, i.e.:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
struct Type {};
template <typename T>
class MyBatchAllocator {
};
template <typename TAllocator = std::allocator<Type>>
class TestClass {
public:
TestClass(signed char, const std::vector<Type, TAllocator>&) {
// ...
}
};
int main()
{
// main.cpp
Type element;
std::vector<Type, MyBatchAllocator<Type>> lst({ element });
TestClass<MyBatchAllocator<Type>> tc(0, lst);
}
Otherwise, if you use standard allocators use this:
int main() {
Type element;
std::vector<Type> lst2({ element });
TestClass<> tc2(0, lst2);
// Yes, if TestClass is a template with a single
// defaulted template parameter the empty <> are required.
}
Appendix
If you want to make your life with those templates easier, you could employ an alias:
using DefaultTestClass = TestClass<>;
and call it like:
DefaultTestClass tc3(0, lst2);
http://en.cppreference.com/w/cpp/language/type_alias
Related
This is just a minimal reproducible example:
import <unordered_set>;
import <functional>;
template<class c>
class my_class {
};
template<class c>
struct std::hash<my_class<c>> {
std::size_t operator()(my_class<c> const &s) const noexcept {
return 0;
}
};
int main() {
std::unordered_set<my_class<char>> x;
}
This code, when compiled with g++ -std=c++20 -fmodules-ts test.cpp produces this error:
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\SOURAV~1\AppData\Local\Temp\cclOkCUA.o:test.cpp:(.text$_ZNSt16allocator_traitsISaIPNSt8__detail15_Hash_node_baseEEE10deallocateERS3_PS2_y[_ZNSt16allocator_traitsISaIPNSt8__detail15_Hash_node_baseEEE10deallocateERS3_PS2_y]+0x2d): undefined reference to `std::is_constant_evaluated()'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\SOURAV~1\AppData\Local\Temp\cclOkCUA.o:test.cpp:(.text$_ZNSt16allocator_traitsISaINSt8__detail10_Hash_nodeI8my_classIcELb0EEEEE10deallocateERS5_PS4_y[_ZNSt16allocator_traitsISaINSt8__detail10_Hash_nodeI8my_classIcELb0EEEEE10deallocateERS5_PS4_y]+0x2d): undefined reference to `std::is_constant_evaluated()'
collect2.exe: error: ld returned 1 exit status
Instead if I define template argument directly in hash struct, there is no error!
template<>
struct std::hash<my_class<char>> {
std::size_t operator()(my_class<char> const &s) const noexcept {
return 0;
}
};
I am confused what is the error!
I think it is just some bug in GCC modules implementation as suggested by #ildjarn in comments.
I was able to get rid of error by adding this line in the source code:
constexpr bool std::is_constant_evaluated() noexcept;
But I'm not sure what exactly was wrong or why did adding this solved the error.
I'm trying to compile a sample program in C++ using templates. The template class has a priavte static member variable which seems to be undefined when trying to compile. Going through other answers on SO, I realized that this variable needs to be defined as well. However my attempts at defining this variable have been unsuccessful so far, likely due to my lack of experience working with templates. Here is my sample program:
#include <iostream>
#include <array>
enum FRUIT
{
APPLE,
ORANGE
};
using FunctionPtr = void(*)(void);
template <FRUIT T>
void FruitFunction(void);
template <FRUIT...TotalFruits>
class TestClass
{
public:
struct fruitGroup
{
FRUIT fruit;
FunctionPtr func;
};
static int find_fruit(FRUIT fruit, int arg)
{
for (auto i = pv_mem_.begin(); i != pv_mem_.end(); ++i) {
if (i->fruit == fruit) {
break;
}
}
return 0;
}
private:
constexpr static std::array<fruitGroup, sizeof...(TotalFruits)> pv_mem_
{
fruitGroup{TotalFruits, &FruitFunction<TotalFruits>}...
};
};
int main()
{
TestClass<FRUIT::APPLE, FRUIT::ORANGE> test;
test.find_fruit(FRUIT::APPLE, 0);
return 0;
}
This yields:
$ g++ -std=c++11 fruit.cpp -o foo
/tmp/ccqaSBYm.o: In function `TestClass<(FRUIT)0, (FRUIT)1>::find_fruit(FRUIT, int)':
fruit.cpp:(.text._ZN9TestClassIJL5FRUIT0ELS0_1EEE10find_fruitES0_i[_ZN9TestClassIJL5FRUIT0ELS0_1EEE10find_fruitES0_i]+0xf): undefined reference to `TestClass<(FRUIT)0, (FRUIT)1>::pv_mem_'
fruit.cpp:(.text._ZN9TestClassIJL5FRUIT0ELS0_1EEE10find_fruitES0_i[_ZN9TestClassIJL5FRUIT0ELS0_1EEE10find_fruitES0_i]+0x1d): undefined reference to `TestClass<(FRUIT)0, (FRUIT)1>::pv_mem_'
collect2: error: ld returned 1 exit status
I have tried defining pv_mem_ as:
constexpr static std::array<TestClass::fruitGroup, sizeof...(TotalFruits)> pv_mem_;
but that resulted in the following error:
$ g++ -std=c++11 fruit.cpp -o foo
fruit.cpp:44:74: error: wrong number of template arguments (1, should be 2)
constexpr static std::array<TestClass::fruitGroup, sizeof...(TotalFruits)> pv_mem_;
^
In file included from fruit.cpp:2:0:
/usr/include/c++/5/array:89:12: note: provided for ‘template<class _Tp, long unsigned int _Nm> struct std::array’
struct array
^
fruit.cpp:44:76: error: uninitialized const ‘pv_mem_’ [-fpermissive]
constexpr static std::array<TestClass::fruitGroup, sizeof...(TotalFruits)> pv_mem_;
^
What would be the right way to initialize this variable?
pv_mem_ is defined as follows
constexpr static std::array<fruitGroup, sizeof...(TotalFruits)> pv_mem_
{
fruitGroup{TotalFruits, &FruitFunction<TotalFruits>}...
};
which uses & to take the address of FruitFunction<TotalFruits>, but since FruitFunction is only declared and not defined, it will generate an undefined reference error at runtime.
Adding the definition for the template function FruitFunction will solve the problem in C++17
template <FRUIT T>
void FruitFunction() { /* */ }
In C++11, constexpr static member variables still need to be defined outside the class, so you also need to add
template <FRUIT...TotalFruits>
constexpr std::array<
typename TestClass<TotalFruits...>::fruitGroup,
sizeof...(TotalFruits)> TestClass<TotalFruits...>::pv_mem_;
Demo
I'm trying to compile code like this using both GCC 4.4.7 and MSVC 2010:
// test.cpp
// use g++ -c test.cpp to attempt compile this
#include <map>
#include <numeric>
#include <algorithm>
struct A
{
A() : v1(0), v2(0) {}
int v1, v2;
};
typedef std::map<int, A> A_map;
void fill_map(A_map& m); // external function
A process()
{
A_map m;
fill_map(m);
A a;
if(!m.empty())
{
struct Aggregate
{
A operator()(const A& left, const A_map::value_type& right)
{
A result;
result.v1 = left.v1 + right.second.v1;
result.v2 = std::max(left.v2, right.second.v2);
return result;
}
};
A a0;
a = std::accumulate(m.begin(), m.end(), a0, Aggregate());
}
return a;
}
While MSVC2010 compiles this nicely, GCC 4.4.7 gives following error:
test.cpp: In function ‘A process()’:
test.cpp:33: error: no matching function for call to ‘accumulate(std::_Rb_tree_iterator<std::pair<const int, A> >, std::_Rb_tree_iterator<std::pair<const int, A> >, A&, process()::Aggregate)’
Any ideas why so and how to fix this?
Ideas like completely rewrite code using C++11 lambdas do not work - need exactly this code.
C++03 does not allow for the instantiation of templates with local types. If you can't use C++11 or C++14, you can fix that problem by moving the definition of Aggregate outside of the process function. For good measure, make its operator() a const member.
#include <map>
#include <numeric>
#include <algorithm>
struct A
{
A() : v1(0), v2(0) {}
int v1, v2;
};
struct Aggregate
{
A operator()(const A& left, const A_map::value_type& right) const
{
....
}
};
void fill_map(A_map& m); // external function
A process()
{
....
}
I have a problem compiling a template using msvc-2010. It works perfectly using gcc 4.6.3.
I have boiled down the code to the essential (it doesn't make sense of course):
//Variant that works
template <typename T, T* Ptr>
void callFun()
{
}
//Traits class (type expands to the same type T* as above)
template <typename T>
class TraitsClass
{
public:
typedef T* type;
};
//Essentially the same as callFun2, only that the
//type of Ptr is expressed indirectly over a traits class
//The usage of this class is not possible, because of the error described below
template <typename T, typename TraitsClass<T>::type Ptr>
void callFun2()
{
}
//Provides a compile constant ptr for this example
void testFun()
{
}
int main()
{
//Works
callFun<void(), &testFun>();
//Fails
callFun2<void(), &testFun>();
//Works
callFun2<void(), 0>();
return 0;
}
The Error:
error C2975: 'Ptr' : invalid template argument for 'callFun2', expected compile-time constant expression
I find it interesting, that it only fails when the second type parameter is being used through a typedef in a Traits class.
g++ compiles this example correctly without warnings, even when using -Wall -Wextra -Werror -pedantic (Except for the unused parameters, of course)
Thank you very much.
Well, I think that the answer is that compilers are not written by gods. Programming standards in the compiler industry are extremely high, MS C++ is a good compiler, but it still contain bugs. I came across the following, that is somehow similar to what you are pointing at:
template <class item_struct>
struct THeapBasedArray
{
void Sort(int (__cdecl *compareFunction)(const item_struct *item1,
const item_struct *item2));
};
struct Item { int x; };
struct ItemPtrsArray : public THeapBasedArray<Item*>
{
static int __cdecl Compare1(const Item **pp1, const Item **pp2);
typedef Item *ItemPtr;
static int __cdecl Compare2(const ItemPtr *pp1, const ItemPtr *pp2);
};
int main()
{
ItemPtrsArray vect;
vect.Sort(ItemPtrsArray::Compare1);
vect.Sort(ItemPtrsArray::Compare2);
}
The first call to Sort fails with:
cpptest1.cxx(21) : error C2664: 'THeapBasedArray::Sort' : cannot convert parameter 1 from 'int (_cdecl *)(const Item **, const Item **)' to 'int (_cdecl *)(const item_struct *, const item_struct *)
while the second call compilers fine. For me this is a bug in a compiler. Sometimes this happens. I guess this is the answer.
I want to make functor to generic function, but I get compiler error.
Here is the code:
template <class T>
struct Creator
{
template <typename...Ts>
static std::shared_ptr<T> create(Ts&&... vs)
{
std::shared_ptr<T> t(new T(std::forward<Ts>(vs)...));
return t;
}
};
class Car:
public Creator<Car>
{
private:
friend class Creator<Car>;
Car()
{
}
};
int main()
{
auto car=Car::create();
std::function< std::shared_ptr<Car> () > createFn=&Car::create;
return 0;
}
I get the following error in GCC 4.6.3 on the second statement(the first is OK):
error: conversion from ‘<unresolved overloaded function type>’
to non-scalar type ‘std::function<std::shared_ptr<Car>()>’ requested
Any hint appreciated.
If the pointer of a template function is needed, the template must be instantiated first.
std::function<std::shared_ptr<Car>()> createFn = &Car::create<>;
This will make it compile on clang++ 3.1, but g++ 4.8 still refuses to compile, which I believe is a bug.
You could provide a lambda function instead:
std::function<std::shared_ptr<Car>()> createFn = []{ return Car::create(); };