Initialize a template class private static variable in C++ - 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

Related

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{};

Clang fails to initialize static const template member

In order to force the execution of a template method at program start one can initialize a static member with a static method. Then the method is run at program start for every instantiation of the template class:
#include <cstdio>
template<typename t, t value>
struct dummy_user_t {};
template<int i>
struct my_struct_t
{
static int s_value;
// "use" s_value so it's initialized
using value_user_t = dummy_user_t<const int&, s_value>;
static int method()
{
printf("Hello %i!\n", i);
return 0;
}
};
// initialize s_value with method() to run it at program start
template<int i>
int my_struct_t<i>::s_value {my_struct_t<i>::method()};
// instantiate my_struct_t
template struct my_struct_t<6>;
int main()
{
// nothing here
}
The output will be Hello 6!
This code compiles on all three major compilers but when you make s_value const it won't work in clang anymore (3.4 - 7.0) while still working in MSVC and GCC:
<source>:19:52: error: no member 'method' in 'my_struct_t<6>'; it has not yet been instantiated
const int my_struct_t<i>::s_value {my_struct_t<i>::method()};
^
<source>:10:51: note: in instantiation of static data member 'my_struct_t<6>::s_value' requested here
using value_user_t = dummy_user_t<const int&, s_value>;
^
<source>:21:17: note: in instantiation of template class 'my_struct_t<6>' requested here
template struct my_struct_t<6>;
^
<source>:11:16: note: not-yet-instantiated member is declared here
static int method()
^
1 error generated.
Try it out yourself:
With non const int: https://godbolt.org/z/m90bgS
With const int: https://godbolt.org/z/D3ywDq
What do you think? Is there any reason clang is rejecting this or is it a bug?

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

Clang++-3.7 CRTP compilation error "no named member" in parent's template argument

In the below code I am trying to use CRTP to use the static member "value" from the Child class in the Parent class. When compiling the code with g++ 5.2.1 with the "-pedantic" flag, I am able to compile as expected, and on execution both c.print_value(); and Child<int,4>::print_value(); print out 4.
#include <iostream>
template <typename DE>
struct Parent
{
static const int value = DE::value;
static void print_value ()
{
std::cout << "Value : " << value << '\n';
}
};
template <typename T, int N>
struct Child : Parent< Child<T,N> >
{
static const int value = N;
};
int
main ()
{
Child<int,4> c;
c.print_value();
Child<int,4>::print_value();
}
However when compiling the same code with clang++3.7, I encounter compilation failures.
crtp_clang_error.cpp:9:32: error: no member named 'value' in 'Child<int, 4>'
static const int value = DE::value;
~~~~^
crtp_clang_error.cpp:27:16: note: in instantiation of template class 'Parent<Child<int, 4> >' requested here
struct Child : Parent< Child<T,N> >
^
crtp_clang_error.cpp:38:16: note: in instantiation of template class 'Child<int, 4>' requested here
Child<int,4> c;
^
crtp_clang_error.cpp:40:3: error: no member named 'print_value' in 'Child<int, 4>'; did you mean 'Parent<Child<int, 4> >::print_value'?
Child<int,4>::print_value();
^~~~~~~~~~~~~~~~~~~~~~~~~
Parent<Child<int, 4> >::print_value
crtp_clang_error.cpp:11:15: note: 'Parent<Child<int, 4> >::print_value' declared here
static void print_value ()
I am not sure if this a Clang++ bug or a GCC hack. Would very much appreciate some insights.

C++ GCC Why this sfinae code can be compiled with GCC 4.7, but not with 4.8?

I like to use local classes in template classes to perform constructions like "static if". But I've faced with the problem that gcc 4.8 does not want to compile my code. However 4.7 does.
This sample:
#include <type_traits>
#include <iostream>
#include <string>
using namespace std;
struct A {
void printA() {
cout << "I am A" << endl;
}
};
struct B {
void printB() {
cout << "I am B" << endl;
}
};
template <typename T>
struct Test {
void print() {
struct IfA {
constexpr IfA(T &value) : value(value) {
}
T &value;
void print() {
value.printA();
}
};
struct IfB {
constexpr IfB(T &value) : value(value) {
}
T &value;
void print() {
value.printB();
}
};
struct Else {
constexpr Else(...) {}
void print() {
}
};
typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print();
typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print();
}
T value;
};
int main() {
Test<A>().print();
Test<B>().print();
}
Options:
g++ --std=c++11 main.cc -o local-sfinae
Task:
Given classes A and B with different interfaces for printing.
Write a generic class Test that can print both A and B.
Do not pollute either any namespace or class scope.
Description of the code:
This is only a clean example.
I use an approach like this, because I want to generalize the construction "static if". See, that I pass the arguments to IfA and IfB classes via their fields, not directly to the print() function.
I use such constructions a lot.
I've found that these constructions should not be in (pollute) class scope. I mean they should be placed in a method scope.
So the question.
This code can not be compiled with GCC 4.8. Because it checks ALL classes, even if they are never used. But it has not instantiate them in binary (I've commented the lines that cause errors and compiled it with gcc 4.8). Proof:
$ nm local-sfinae |c++filt |grep "::If.*print"
0000000000400724 W Test<A>::print()::IfA::print()
00000000004007fe W Test<B>::print()::IfB::print()
See, there is no Test::print()::IfB::print(). (See later: 'void Test::print()::IfB::print() [with T = A]')
The errors if I compile aforementioned code with gcc 4.8:
g++ --std=c++11 main.cc -o local-sfinae
main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]':
main.cc:36:9: required from 'void Test<T>::print() [with T = A]'
main.cc:49:21: required from here
main.cc:34:17: error: 'struct A' has no member named 'printB'
value.printB();
^
main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]':
main.cc:28:9: required from 'void Test<T>::print() [with T = B]'
main.cc:50:21: required from here
main.cc:26:17: error: 'struct B' has no member named 'printA'
value.printA();
^
Is it a GCC 4.8 bug?
Or is it GCC 4.7 bug? Maybe the code should not be compiled.
Or it is a my bug, and I should not rely on the compiler behavior/should not use such approach to implement "static if".
Additional info:
This simple code compiles on 4.7, but not on 4.8. I shortened it.
struct A {
void exist() {
}
};
template <typename T>
struct Test {
void print() {
struct LocalClass {
constexpr LocalClass(T &value) : value(value) {
}
T &value;
void print() {
value.notExist();
}
};
}
T value;
};
int main() {
Test<A>().print();
}
Errors:
main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]':
main.cc:16:9: required from 'void Test<T>::print() [with T = A]'
main.cc:22:21: required from here
main.cc:14:17: error: 'struct A' has no member named 'notExist'
value.notExist();
^
Have tested two GCC 4.8 versions: 2012.10 and 2013.02. Hope it is GCC 4.8 bug and it can be fixed.
LocalClass is not a template. The "not instantiated if not used" rule is only applicable to member functions of class templates.
That is, when Test::print() is instantiated, everything that is inside is brought to life, including the unused member of its local class.
There is no SFINAE in your code.
SFINAE applies during template argument deduction and argument substitution (the 'S' in SFINAE stands for substitution) but the only substitution in your program happens when substituting A for T in the template parameter list of Test, which doesn't fail.
You then call print() which instantiates Test<A>::print(), which doesn't involve any substitution, and you get an error because value.notExist(); is not valid.
SFINAE has to be used in substitution contexts, such as template argument deduction caused by a function call or when deducing template parameters with default arguments.