Consider the program: ========================================= template<int D> class Foo { public: typedef double double3[3]; }; template<int D> void doSomething(const typename Foo<D>::double3) { } template void doSomething<2>(const Foo<2>::double3); template void doSomething<3>(const Foo<3>::double3); ============================================ On Mac os X 10.5 it fails with gcc-4.4.0 (and also with the default gcc 4.0.1) with the error message > /usr/local/gcc-4.4.0/bin/g++ -v test.cc Using built-in specs. Target: i686-apple-darwin9 Configured with: ./configure --prefix=/usr/local/gcc-4.4.0 --disable-checking -enable-werror --enable-languages=c,c++,fortran --build=i686-apple-darwin9 --with-tune=generic --host=i686-apple-darwin9 --target=i686-apple-darwin9 Thread model: posix gcc version 4.4.0 (GCC) COLLECT_GCC_OPTIONS='-mmacosx-version-min=10.5.6' '-v' '-shared-libgcc' '-mtune=generic' /usr/local/gcc-4.4.0/libexec/gcc/i686-apple-darwin9/4.4.0/cc1plus -quiet -v -D__DYNAMIC__ test.cc -fPIC -quiet -dumpbase test.cc -mmacosx-version-min=10.5.6 -mtune=generic -auxbase test -version -o /var/folders/jq/jqHHJIlXEbOleEkgf132gE+++TI/-Tmp-//cctUrpft.s ignoring nonexistent directory "/usr/local/gcc-4.4.0/lib/gcc/i686-apple-darwin9/4.4.0/../../../../i686-apple-darwin9/include" #include "..." search starts here: #include <...> search starts here: /usr/local/gcc-4.4.0/lib/gcc/i686-apple-darwin9/4.4.0/../../../../include/c++/4.4.0 /usr/local/gcc-4.4.0/lib/gcc/i686-apple-darwin9/4.4.0/../../../../include/c++/4.4.0/i686-apple-darwin9 /usr/local/gcc-4.4.0/lib/gcc/i686-apple-darwin9/4.4.0/../../../../include/c++/4.4.0/backward /usr/local/include /usr/local/gcc-4.4.0/include /usr/local/gcc-4.4.0/lib/gcc/i686-apple-darwin9/4.4.0/include /usr/local/gcc-4.4.0/lib/gcc/i686-apple-darwin9/4.4.0/include-fixed /usr/include /System/Library/Frameworks /Library/Frameworks End of search list. GNU C++ (GCC) version 4.4.0 (i686-apple-darwin9) compiled by GNU C version 4.4.0, GMP version 4.2.1, MPFR version 2.3.0. GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: 4703a3934dc941165107f7c66932f1e1 test.cc:15: error: template-id 'doSomething<2>' for 'void doSomething(const double*)' does not match any template declaration test.cc:18: error: template-id 'doSomething<3>' for 'void doSomething(const double*)' does not match any template declaration Removing the const clarifier gives no errors.
Hm, interesting. I would have thought as well that the code should compile. On the other hand, icc also produces the same error message, so I am now officially confused...
IIUC, the top-level const qualifier on "const typename Foo<D>::double3" in the primary template applies to the type, which is not known during phase 1 At instantiation time, double3 is known to be an array type, so decays to a pointer, and the parameter type becomes "const double*" _not_ "double* const" So the explicit instantiation creates a type which does not match the primary template, so it cannot be a specialisation of it.
(In reply to comment #2) > IIUC, the top-level const qualifier on "const typename Foo<D>::double3" in the > primary template applies to the type, which is not known during phase 1 i.e. it's equivalent to "typename Foo<D>::double3 const" which is equivalent to simply "typename Foo<D>::double3) because top-level cv-qualifiers are removed from function parameters. but when double3 is known to be an array type, which is replaced by a pointer in a parameter list, then the declaration's meaning changes. Consider: typedef double double3[3]; void f(const double3); typedef void (*ptr)(const double*); ptr p = f; This demonstrates that the declaration of 'f' is equivalent to: void f(const double*); not void f(double* const); According to [dcl.fct] array parameters are replaced with pointers before top-level cv-qualifiers are deleted. I think GCC is correct, the template specializations are invalid.
Thanks Jonathan for your explanation. Now I can understand the reason behind this... (though I'm not really happy with the fact that you need to understand sort of internals to see why code doesn't compile). Do I need to mark the bug-report as invalid, or how does it work? Thanks!
I think this is a bug; we shouldn't be dropping the const at template parsing time.
Actually, I'm not so sure: template<class T> struct A { typedef A arr[3]; }; template<class T> void f(const typename A<T>::arr) { } template void f<int>(const A<int>::arr); // #1 template <class T> struct B { void g(T); }; template <class T> void B<T>::g(const T) { } // #2 it seems to me that either #1 is well-formed, or #2 is, though it might make sense to recompute the function type from the parameter type...in any case, I'm opening a DR about this.
Created attachment 18996 [details] patch Here's a patch that fixes the testcase and passes regression testing, but I don't think it's the right way to go.
Jason, the extra const in the library code is definitely unintended, I'm going to remove it anyway.
Suspending until the committee can rule on this, but I expect the result to be that the testcase is ill-formed.
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1001 is NAD so I think we can close this bug as INVALID
Agreed.