ordering deferred instantiation

the following fails to compile in gcc7 since the compiler takes declaration-order as more important than instantiation order:
--------------------------------------------------------
#include <array>

template <class T>
inline void Fail_(T *t = 0) { if(t) Fail0_(t); }

template<class T,long unsigned int N> inline void Fail0_(std::array<T,N> *t) {}

void delme() {Fail_((std::array<short int,0> *)0);}
---------------------------------------------------------

however, removing the last line will get me a successful compilation! alternatively, adding some Fail0_() template before Fail_ will unconditionally choose that instead of the function declared below Fail_(). is this proper c++17 behaviour? is it undefined behaviour? I remember gcc4 had a different opinion...
Have you tried to move the definition of Fail_() to after the definition of Fail0_()?

1
2
3
4
5
6
7
8
#include <array>

template<class T,long unsigned int N> inline void Fail0_(std::array<T,N> *t) {}

template <class T>
inline void Fail_(T *t = 0) { if(t) Fail0_(t); }

void delme() {Fail_((std::array<short int,0> *)0);}
yes, I tried both ways. your code compiles, mine doesn't! in practice it means overloading/redefinition of Fail0_() must happen before including a file where template<>Fail_() is defined, or just overload Fail_() instead, moving the problem up by one level (i.e. you can't do that after definition of a templated delme())...
in practice it means overloading/redefinition of Fail0_() must happen before including a file where template<>Fail_() is defined, or just overload Fail_() instead, moving the problem up by one level (i.e. you can't do that after definition of a templated delme())...

What? Since you're dealing with templates it is best if all of the specializations and "overloads" of those templates be in the same compilation unit (usually the same include file).

The problem is that in the definition of Fail_() you are using Fail0_() so that means that Fail0_() must be declared prior to the definition of Fail_().

The same with delme(), it is using Fail_() in the body of the definition therefore Fail_() must be declared prior to the definition of delme().


thanks, this answers my original question. but as for "same include file":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <array>

template<class C>
void sort(C& c) {std::cout<<"mysort3"<<std::endl;}

template<class T>
struct s{
	s(T& cont) {sort(cont);}
};

//---------------------------------------------------------------
template<> void sort<std::array<int,0>>(std::array<int,0>&) 
{std::cout<<"my sort"<<std::endl;}
template<typename T,long unsigned N> void sort(std::array<T,N>&) 
{std::cout<<"mysort2"<<std::endl;}

int main(...)
{
	std::array<int,0> a;
	std::array<int,1> b;
	s tmp{a};//output: "my sort"
	sort(a); //yields "mysort2"
	s tmp2{b};//here  "mysort3"
}
your explanations helped me understand this program (well, except for the line with sort(a), there prioritization isn't clear). but a new question did arise: if you write a library and the stuff above the "------" line is the include-file, how do you allow the users to implement their own sort() specializations? the way it is now, the first overload would need to get repeated for each and every value "long unsigned" could ever have. other containers wouldn't have that problem though. does sort() need to be an executable class instead of a function? or should I wait for partial specializations of functions?

Edit: nevermind, guess I need to use classes as functions here:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <array>

template<long unsigned N> struct static_num{constexpr static auto val=N; };

template<class C,class... Ts>
struct sort{
	sort(C& c) {std::cout<<"mysort3"<<std::endl;}
};

template<class T>
struct s{
	s(T& cont) {sort{cont};}
};

template<> struct sort<std::array<int,0>>{
sort(std::array<int,0>&) {std::cout<<"my sort"<<std::endl;}};
template<typename T,typename N> struct sort<std::array<T,N::val>,T,N>{
sort(std::array<T,N::val>&) {std::cout<<"mysort2"<<std::endl;}};
template<long unsigned N> 
sort(std::array<int,N>&) -> sort<std::array<int,N>,int,static_num<N>>;
sort(std::array<int,0>&) -> sort<std::array<int,0>>;

int main(...)
{
	std::array<int,0> a;
	s tmp{a};//output: "my sort"
	sort{a}; //same
	std::array<int,1> b;
	s tmp2{b};//yields "mysort2"
}
Last edited on
The problem you're having is part of the problem with using std::array, the size must be a compile time constant.

std::array<int,1> is different than std::array<int, 2>. But what you can do for your sort is make the size a template argument instead. But it would probably be much easier to just use std::sort instead.

By the way your program doesn't compile for me:

1
2
3
4
5
6
||=== Build: Debug in testcpp (compiler: gcc 6.1.0) ===|
main.cpp||In function ‘int main(...)’:|
main.cpp|139|error: missing template arguments before ‘tmp’|
main.cpp|141|error: missing template arguments before ‘tmp2’|
main.cpp|138|warning: unused variable ‘b’ [-Wunused-variable]|
||=== Build failed: 2 error(s), 1 warning(s) (0 minute(s), 1 second(s)) ===|


Line 139 is your line 20.




probably should have mentioned: I use "g++-7.2.0 -std=c++17"
Edit:to run in c++shell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <array>

template<class C>
void sort(C& c) {std::cout<<"mysort3"<<std::endl;}

template<class T>
struct s{
	s(T& cont) {sort(cont);}
};

//---------------------------------------------------------------
template<> void sort<std::array<int,0>>(std::array<int,0>&) 
{std::cout<<"my sort"<<std::endl;}
template<typename T,long unsigned N> void sort(std::array<T,N>&) 
{std::cout<<"mysort2"<<std::endl;}

int main(...)
{
	std::array<int,0> a;
	std::array<int,1> b;
	s<std::array<int,0>> tmp{a};//output: "my sort"
	sort(a); //yields "mysort2"
	s<std::array<int,1>> tmp2{b};//here  "mysort3"
}

as I implied: I have no idea how to implement the 2nd approach for c++14 or old g++-6.1.0 (which isn't ready for c++17 yet)...
Last edited on
Topic archived. No new replies allowed.