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?
Related
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
The code below compiles on MSVC but fails on GCC (4.6.3). Why does it fail and what should I do to fix it?
#include <array>
class Foo {
public:
template<typename T, int N>
operator std::array<T, N>() const {
return std::array<T, N>();
}
};
int main(){
Foo val;
// both of the following lines fail on GCC with error:
// "no matching function call...", ultimately with a note:
// "template argument deduction/substitution failed"
auto a = val.operator std::array<int, 2>();
static_cast<std::array<int, 2>>(val);
return 0;
}
EDIT: The following code, however, does compile (on both compilers), despite passing in an int for std::array's template parameter.
template<int N, typename T>
struct Bar {
std::array<T, N> buf;
};
int main()
{
auto x = Bar<3, double>();
return 0;
}
If you read the full text of the error messages you get, the compiler is complaining because the type for N in your template class is int, while the second parameter of std::array is std::size_t, which is an unsigned long on your system.
Changing your template's declaration to use std::size_t N will fix the problem.
MSVC is not complaining possibly because it recognizes that the value "2" works for either case, or because of a compiler bug.
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.
I have the following piece of code which represents an actual bigger piece of code:
#include <iostream>
using namespace std;
template<size_t N> class A {
public:
static constexpr size_t getN() {return N;}
};
template<size_t N> class B {
public:
void print() { cout << "B created: " << N << '\n';}
};
template <class T> class C {
public:
void set(T* a) {
t_ptr = a;
}
void create() {
constexpr int m = t_ptr->getN();
B<m> b;
b.print();
}
private:
T* t_ptr;
};
int main() {
constexpr int n = 2;
A<n> a;
C<A<n> > c;
c.set(&a);
c.create();
}
Compiling with g++ -o main main.cpp -std=c++11 and GCC/G++ 4.8.3 the expected output is received:
B created: 2
However, with GCC/G++ 4.9.1 the code does not compile, output:
main.cpp: In member function ‘void C<T>::create()’:
main.cpp:27:15: error: the value of ‘m’ is not usable in a constant expression
B<m> b;
^
main.cpp:26:27: note: ‘m’ used in its own initializer
constexpr int m = t_ptr->getN();
^
main.cpp:27:16: error: the value of ‘m’ is not usable in a constant expression
B<m> b;
^
main.cpp:26:27: note: ‘m’ used in its own initializer
constexpr int m = t_ptr->getN();
^
main.cpp:27:19: error: invalid type in declaration before ‘;’ token
B<m> b;
^
main.cpp:28:15: error: request for member ‘print’ in ‘b’, which is of non-class type ‘int’
b.print();
^
This is caused by a known bug in GCC 4.9: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59937 and in this older thread https://gcc.gnu.org/ml/gcc-bugs/2013-11/msg00067.html the usage of extern is proposed as a workaround. However, I am not able to get this workaround working.
Could you guys help me to make this code compile in GCC 4.9? Thank you!
Since this is not constexpr the access to this->t_ptr is not either.
clang's error is a bit more helpful
implicit use of 'this' pointer is only allowed within the
evaluation of a call to a 'constexpr' member function
Referring to:
N3690 5.19/2 (emphasis added)
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
— this, except in a constexpr function or a constexpr constructor that is being evaluated as
part of e;
Calling the static member function via the typename works
constexpr int m = T::getN();
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.