3 ms·
~/test/O$ cat break.cpp #include <iostream> template <typename A, typename B> struct MutualRecursion { static const int a = MutualR
by knome 13y ago
~/test/O$ cat break.cpp
#include <iostream>
template <typename A, typename B>
struct MutualRecursion {
static const int a = MutualRecursion< B, A >::b ;
static const int b = MutualRecursion< B, A >::a ;
};
int main( int argc, char ** argv ){
std::cout << MutualRecursion< float, int >::b << std::endl;
return 0;
};
~/test/O$ clang break.cpp
break.cpp:6:49: error: no member named 'b' in 'MutualRecursion<float, int>'
static const int a = MutualRecursion< B, A >::b ;
~~~~~~~~~~~~~~~~~~~~~~~~~^
break.cpp:6:24: note: in instantiation of template class 'MutualRecursion<int, float>' requested here
static const int a = MutualRecursion< B, A >::b ;
^
break.cpp:11:16: note: in instantiation of template class 'MutualRecursion<float, int>' requested here
std::cout << MutualRecursion< float, int >::b << std::endl;
^
break.cpp:7:24: error: in-class initializer is not a constant expression
static const int b = MutualRecursion< B, A >::a ;
^~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.
~/test/O$
~~This is definitely a bug in clang~~. clang doesn't find the member "b" here because it's referencing the cached still-in-construction version of the template. Furthermore, it still does this even when "b" is a regular constant.
// static const int b = MutualRecursion< B, A >::a ;
static const int b = 10 ;
gcc, since it appears to always instantiate a new copy of the template compiles successfully for when "b = 10".
~~clang needs to wait until it has fully constructed a type before putting it into the cache, even if it might be slower in some recursive cases. Better than wrong in them.~~
I don't know if clang is in the wrong here, so I'm rejecting a couple of statements. Its method does not allow for compilation in these circumstances, but may be technically permissible, even if gcc jives with how I envision templates operating.
- pbsd 13y agoI don't see how that could not be a compiler error. The case b = 10 is more interesting because, as far as I can tell, it shows the differences between compilers that do two-phase lookup correctly and those that don't. This seems to be a rare case where GCC is in the right, but I'd have to double-check the standard to be sure.
- knome 13y agoIt was pointed out to me after I joined #llvm when considering reporting this that using the value of "b", which is defined after "a" in the class before it has been initialized is, I suppose obviously in retrospect, undefined behavior. In effect, I was subverting the undefined behavior associated with expanding a set of infinitely deep template instantiations by accessing a variable that was as yet undefined at the point of access. So, my initial retractions stand, and this compiler behavior will remain appropriately undefined.