The following sequence applies to recursion to operate on a specific nest level in a nested template object in held in a data parallel container class. I lifted out of an expression template engine I'm using where I might want to, for example, transpose one index of a container of multi-dimensional arrays.
I believe the code is unambiguous under SFINAE;
Containers go to third occurrence of function, failing to match "NestLevel". "Recursive" recurses through the second occurrence of function, until the Nest Level matches and it the recursion terminates on the first occurrence of function.
#include <vector>
#include <complex>
#include <type_traits>
#include <iostream>
typedef std::complex<double> ComplexD;
template <class T> class TypeMapper {
public:
enum { NestLevel = T::NestLevel };
};
template<> class TypeMapper<ComplexD> {
public:
enum { NestLevel = 0 };
};
template<class obj> class Container {
public:
std::vector<obj> data;
Container(int size) : data (size){};
};
template<class obj> class Recursive {
public:
enum { NestLevel = TypeMapper<obj>::NestLevel + 1};
obj internal;
};
template<int N,class obj,typename std::enable_if<N==obj::NestLevel >::type * = nullptr > auto function(const obj &arg)-> obj
{
std::cout<<"Leaf "<<obj::NestLevel<<std::endl;
return arg;
}
template<int N,class obj,typename std::enable_if<N!=obj::NestLevel >::type * = nullptr > auto function(const obj &arg)-> obj
{
std::cout<<"Node "<<obj::NestLevel<<std::endl;
obj ret;
ret.internal=function<N>(arg.internal);
return ret;
}
template<int N,class obj> auto function(const Container<obj> & arg)-> Container<decltype(function<N>(arg.data[0]))>
{
Container<decltype(function<N>(arg.data[0]))> ret(arg.data.size());
for(int ss=0;ss<arg.data.size();ss++){
ret.data[ss] = function<N>(arg.data[ss]);
}
return ret;
}
int main(int argc,char **argv)
{
Container<Recursive<Recursive<ComplexD> > > array(10);
Container<Recursive<Recursive<ComplexD> > > ret(10);
ret = function<1>(array);
}
It works on Intel 15, Clang++ multiple versions delivering what I expect.
pab$ icpc -std=c++11 broken.cc -o broken
pab$ clang++ -std=c++11 broken.cc -o broken
pab$ clang++ --version
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix
pab$ icpc --version
icpc (ICC) 15.0.3 20150408
Copyright (C) 1985-2015 Intel Corporation. All rights reserved.
With the output I expect:
pab$ ./broken
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Node 2
Leaf 1
Then
function(const Container<obj> & arg)
loops over the container, calling the
std::enable_if<N!=obj::NestLevel > function
this recurses, decrementing the nest level enum and then matches on the
std::enable_if<N==obj::NestLevel > function.
However, GCC fails to parse this, instead having function(Container) self call in an infinite recursive loop (4.8, 4.9) and with internal compiler error on 5.0.
pab$ g++-5 -std=c++11 broken.cc -o broken
'
Internal compiler error: Error reporting routines re-entered.
broken.cc: In substitution of 'template<int N, class obj> Container<decltype (function<N>(arg.data[0]))> function(const Container<obj>&) [with int N = 1; obj = <missing>]':
broken.cc:43:101: recursively required by substitution of 'template<int N, class obj> Container<decltype (function<N>(arg.data[0]))> function(const Container<obj>&) [with int N = 1; obj = <missing>]'
broken.cc:43:101: required by substitution of 'template<int N, class obj> Container<decltype (function<N>(arg.data[0]))> function(const Container<obj>&) [with int N = 1; obj = <missing>]'
broken.cc:45:33: Abort trap: 6
template<int N,class obj> auto function(const Container<obj> & arg)-> Container<decltype(function<N>(arg.data[0]))>
^
g++-5: internal compiler error: Abort trap: 6 (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://ift.tt/1QWQDKR; for instructions.`enter code here`
The match looks buggy because function(const Container & arg) shouldn't be recursing to itself because the parameters don't match. It should instead substitution fail and call the other template for function. If I rename these and change the call inside the Container parameter function it compiles.
Templates are hard though -- Is my programme legal and this a compiler bug?
Aucun commentaire:
Enregistrer un commentaire