undefined reference to `std::is_constant_evaluated()' in c++20 gcc - c++

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.

Related

undefined reference issue with latest gcc

I have link-time error when trying to compile following code with gcc 12.1.0.
With clang, msvc and older gcc it compiles as expected.
template<typename T>
void def()
{}
template<void (*foobar)() = def<int>>
void bar()
{
foobar();
}
template<typename T>
void foo()
{
bar();
}
int main()
{
foo<int>();
}
Error: /usr/bin/ld: /tmp/cchkaKVw.o: in function `void bar<&(void def<int>())>()':
> main.cpp:(.text._Z3barIXadL_Z3defIiEvvEEEvv[_Z3barIXadL_Z3defIiEvvEEEvv]+0x5): undefined reference to `void def<int>()'
Is this a gcc regression or is there some problem with this code?
Reported this bug to GCC Bugzilla: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105848

Initialize a template class private static variable in C++

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

How to instantiate a static member of a template class with integer template parameter?

I found various answers to questions about how to instantiate static members of C++ template classes with type parameters (for example https://stackoverflow.com/a/3229904/2995726), but I'm unable to apply this to a template class with integer parameter(s).
I tried this, with the additional complication that the template parameter has a default value:
$ cat test.cc
#include <iostream>
template<unsigned int a = 33>
class A
{
public:
static unsigned char x;
static unsigned int f(void) { return x + a; }
};
// Instantiate template
template class A<>;
// Attempts to instantiate static member:
// unsigned char A<>::x;
// template<> unsigned char A<>::x;
// template<unsigned int> unsigned char A<>::x;
int main(void)
{
A<>::x = 3;
std::cout << A<>::f() << std::endl;
}
When I try to compile this with g++ 10.2, I get a linker error:
$ g++ -std=c++14 -o foo test.cc
/usr/bin/ld: /tmp/ccXGU1fT.o: in function `main':
test.cc:(.text+0x7): undefined reference to `A<33u>::x'
/usr/bin/ld: /tmp/ccXGU1fT.o: in function `A<33u>::f()':
test.cc:(.text._ZN1AILj33EE1fEv[_ZN1AILj33EE1fEv]+0x7): undefined reference to `A<33u>::x'
collect2: error: ld returned 1 exit status
The three commented lines are attempts to instantiate the static member variable. When I enable the following line:
unsigned char A<>::x;
then this happens:
test.cc:16:15: error: specializing member ‘A<>::x’ requires ‘template<>’ syntax
16 | unsigned char A<>::x;
| ^~~~
Using this line:
template<> unsigned char A<>::x;
again results in a linker error:
$ g++ -std=c++14 -o foo test.cc
/usr/bin/ld: /tmp/ccmw49ld.o: in function `main':
test.cc:(.text+0x7): undefined reference to `A<33u>::x'
/usr/bin/ld: /tmp/ccmw49ld.o: in function `A<33u>::f()':
test.cc:(.text._ZN1AILj33EE1fEv[_ZN1AILj33EE1fEv]+0x7): undefined reference to `A<33u>::x'
collect2: error: ld returned 1 exit status
And finally the line:
template<unsigned int> unsigned char A<>::x;
results in this error:
$ g++ -std=c++14 -o foo test.cc
test.cc:18:43: error: template parameters not deducible in partial specialization:
18 | template<unsigned int> unsigned char A<>::x;
| ^
test.cc:18:43: note: ‘<anonymous>’
test.cc:25: confused by earlier errors, bailing out
Instead of "Instantiate template" you need to define the static variable which currently is only declared.
A definition could look like this:
template<unsigned int a>
unsigned char A<a>::x{};
Demo
The problem is that currently we only have a declaration for the static data member x inside the class template. So to solve this, we need to provide a definition for the static data member x. With C++17 you can use inline to define the static data member x inside the class template so that an out-of-class definition of the static data member is not needed anymore, as shown below:
template<unsigned int a = 33>
class A
{
public:
inline static unsigned char x{}; //inline used here
static unsigned int f(void) { return x + a; }
};
Demo
Pre-C++17
For Pre-C++17 standard, we have to provide an out-of-class definition for the static data member:
//out-of-class definition for Pre-C++17
template<unsigned int a>
unsigned char A<a>::x{};

How to solve an unexpect std::allocator when compile code?

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

How to fix linkage error when using template class with static constexpr?

I have the following code
#include <iostream>
template <class T>
class A
{
public:
static constexpr int arr[5] = {1,2,3,4,5};
};
template<> constexpr int A<int>::arr[5];
int main()
{
A<int> a;
std::cout << a.arr[0] << std::endl;
return 0;
}
Compilation passes just fine but I have a linkage error which I don't understand
g++ -std=c++11 test.cpp -o test
/tmp/ccFL19bt.o: In function `main':
test01.cpp:(.text+0xa): undefined reference to `A<int>::arr'
collect2: error: ld returned 1 exit status
You can not just define it for one type, you need
template<class T> constexpr int A<T>::arr[5];