Bug 19288 - No more possible to have a template function that uses a nested class of a template class
Summary: No more possible to have a template function that uses a nested class of a te...
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.0.0
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-01-06 08:40 UTC by jean-pierre.chevallet
Modified: 2005-07-23 22:49 UTC (History)
1 user (show)

See Also:
Host: i386-redhat-linux
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description jean-pierre.chevallet 2005-01-06 08:40:07 UTC
#include <iostream>

using namespace std;

template <typename T> class C1 {
public:
  T V;
  C1(T v) : V(v) {};
  
  class nested {
  public:
    T W;
    nested(T w) : W(w) {};
  };
  
};

// This works well

template <typename T>
T f1 ( C1<T>& c) { return c.V; }

// the problem is HERE

template <typename T>
T f2 ( typename C1<T>::nested& c) { return c.W; }

// This is correct with gcc 3.3.2 20031022
// This is incorrect with gcc 3.4.2 20041017
// This is incorrect with gcc 3.4.3 20041226
// This is incorrect with gcc4 4.0.0 20041228
// Error :
// nestedFriendTemplateFunction.cc:44: error: no matching function for call to
`f2(C1<float>::nested&)'
// Need EXPLICITE instantiation ==> GCC BUG !!!
// float f2 (C1<float>::nested& c) { return c.W; }


int main() {
  C1<int> c(123);
  cout<<f1(c)<<endl;
  
  C1<float>::nested n(3.5);
  cout<<n.W<<endl;
  cout<<f2(n)<<endl;  // CANNOT FIND THE CORRECT CODE FOR THIS
}


So, starting from version 3.4.2 it is no more possible to use a template
function that refers to a nested class of a template class.

g++ versions :
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--disable-checking --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.3 20041226 (Red Hat 3.4.3-11)

Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--enable-checking=release --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --with-gxx-include-dir=/usr/include/c++/3.4.3
--enable-languages=c,c++,java,f95 --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 4.0.0 20041228 (Red Hat 4.0.0-0.17)
Comment 1 Giovanni Bajo 2005-01-06 10:13:04 UTC
This is invalid. Within the expression "C1<T>::nested", T cannot be deduced 
because it is in an nondeduced context. 

ISO/ANSI C++ standard reference: [temp.deduct.type]/4:

The nondeduced contexts are:

  --  The  nested-name-specifier  of  a  type that was specified using a
      qualified-id.

  --  A type that is a template-id in which one or more of the template-
      arguments is an expression that references a template-parameter.

  When  a  type  name  is  specified in a way that includes a nondeduced
  context, all of the types  that  comprise  that  type  name  are  also
  nondeduced.   However,  a  compound  type can include both deduced and
  nondeduced types.  [Example: If a type is  specified  as  A<T>::B<T2>,
  both  T  and  T2  are nondeduced.  Likewise, if a type is specified as
  A<I+J>::X<T>, I, J, and T are nondeduced.  If a type is  specified  as
  void f(typename A<T>::B, A<T>), the T in A<T>::B is nondeduced but the
  T in A<T> is deduced.


Also notice the end of paragraph 3: "If a template parameter is used only in 
nondeduced contexts and is not explicitly specified, template argument 
deduction fails."


Using a specialization as a workaround is a bit bloating. The correct way is to 
explicitly specify the template arguments which cannot be deduced when calling 
the template. In your case, you can use f2<float>(n).
Comment 2 jean-pierre.chevallet 2005-01-06 13:44:19 UTC
Subject: RE:  No more possible to have a template function that uses a nested class of a template class

Thank you very much for your answer, and I'm very impressed : you have
answer very quicky !!

May I ask you what follows my problem : instead of a normal function I
have an operator in the "real" code I try to compile.

The operator is << : but how to explicitly specify template argument
when the tamplate function is an operator ? 

Thank you again !

PS: in fact I'm little disappointed the way C++ evolve during last
years, I feel have more and more difficulties to make all the code I
have in charge compiling when new version of gcc are available,
specially for the templates. Any advice ?

******************************************
Jean-Pierre Chevallet IPAL-CNRS
Image Processing and Application Lab
Institute for Infocomm Research I2R
21 Heng Mui Keng Terrace
Singapore 119613
http://ipal.imag.fr
Jean-Pierre.Chevallet@imag.fr
Tel work: +65 6874 8526
Fax: +65 6775 5014
Tel home: +65 6256 6141


> -----Original Message-----
> From: giovannibajo at libero dot it [mailto:gcc-bugzilla@gcc.gnu.org]
> Sent: jeudi 6 janvier 2005 18:13
> To: jean-pierre.chevallet@imag.fr
> Subject: [Bug c++/19288] No more possible to have a template function
that
> uses a nested class of a template class
> 
> 
> ------- Additional Comments From giovannibajo at libero dot it
2005-01-06
> 10:13 -------
> This is invalid. Within the expression "C1<T>::nested", T cannot be
> deduced
> because it is in an nondeduced context.
> 
> ISO/ANSI C++ standard reference: [temp.deduct.type]/4:
> 
> The nondeduced contexts are:
> 
>   --  The  nested-name-specifier  of  a  type that was specified using
a
>       qualified-id.
> 
>   --  A type that is a template-id in which one or more of the
template-
>       arguments is an expression that references a template-parameter.
> 
>   When  a  type  name  is  specified in a way that includes a
nondeduced
>   context, all of the types  that  comprise  that  type  name  are
also
>   nondeduced.   However,  a  compound  type can include both deduced
and
>   nondeduced types.  [Example: If a type is  specified  as
A<T>::B<T2>,
>   both  T  and  T2  are nondeduced.  Likewise, if a type is specified
as
>   A<I+J>::X<T>, I, J, and T are nondeduced.  If a type is  specified
as
>   void f(typename A<T>::B, A<T>), the T in A<T>::B is nondeduced but
the
>   T in A<T> is deduced.
> 
> 
> Also notice the end of paragraph 3: "If a template parameter is used
only
> in
> nondeduced contexts and is not explicitly specified, template argument
> deduction fails."
> 
> 
> Using a specialization as a workaround is a bit bloating. The correct
way
> is to
> explicitly specify the template arguments which cannot be deduced when
> calling
> the template. In your case, you can use f2<float>(n).
> 
> --
>            What    |Removed                     |Added
>
------------------------------------------------------------------------
--
> --
>              Status|UNCONFIRMED                 |RESOLVED
>          Resolution|                            |INVALID
> 
> 
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19288
> 
> ------- You are receiving this mail because: -------
> You reported the bug, or are watching the reporter.

Comment 3 Wolfgang Bangerth 2005-01-06 14:46:20 UTC
Giving explicit template arguments for template operators works 
the same way: write 
  x.operator<< <float> (abc) 
instead of 
  x << abc 
 
As for the evolution of C++: it isn't C++ but rather gcc that is evolving. 
The standard for C++ was finished in 1998, and since then gcc has just tried 
to be more standards conformant. gcc3.4 was a big step forward in this 
respect. 
 
W. 
Comment 4 Giovanni Bajo 2005-01-06 15:31:39 UTC
My passionate suggestion is to avoid nested classes, but you didn't hear this 
from me ;)
Comment 5 Wolfgang Bangerth 2005-01-06 17:41:01 UTC
Nah, that may be your Eurocentric viewpoint. Over here in Texas one 
thinks differently :-)) 
Comment 6 jean-pierre.chevallet 2005-01-10 03:04:01 UTC
(In reply to comment #3)
> Giving explicit template arguments for template operators works 
> the same way: write 
>   x.operator<< <float> (abc) 
> instead of 
>   x << abc 
>  

Ok, I have test it but with a function operator : it doesn't seems to work.
So what is wrong ??

#include <iostream>

using namespace std;

template <typename T> class C1 {
public:
  T V;
  C1(T v) : V(v) {};
  
  class nested {
  public:
    T W;
    nested(T w) : W(w) {};
  };
  
};

// Template version of operator using nested temptate class
template <typename T>
ostream&  operator << (ostream& out,typename C1<T>::nested& c) {return out<<c.W; }

// Non template version of operator using nested temptate class
// ostream&  operator << (ostream& out, C1<float>::nested& c) {return out<<c.W;}

int main() {
 
  C1<float>::nested n(3.5);
  cout<<n.W<<endl;;

  // Tested with gcc 3.3.2

  // cout<<n<<endl;;       
  // Works with non template, doesn't work with template version

  // operator<<(cout,n)<<endl;; 
  // Works with non template, doesn't work with template version
  
  //cout.operator<< <float> (n);
  // Never works : because << is a function and not a member function

  //operator<< <float> (cout,n);
  // Never work !! but it should ???
}
Comment 7 Wolfgang Bangerth 2005-01-10 04:21:50 UTC
You are trying to overload operator<<(std::ostream, float). That 
can't work, since there is already an overload for that. 
 
W. 
Comment 8 Gabriel Dos Reis 2005-01-10 04:39:22 UTC
Subject: Re:  No more possible to have a template function that uses a nested class of a template class

"jean-pierre dot chevallet at imag dot fr" <gcc-bugzilla@gcc.gnu.org> writes:

| > Giving explicit template arguments for template operators works 
| > the same way: write 
| >   x.operator<< <float> (abc) 
| > instead of 
| >   x << abc 
| >  
| 
| Ok, I have test it but with a function operator : it doesn't seems to work.
| So what is wrong ??
| 
| #include <iostream>
| 
| using namespace std;
| 
| template <typename T> class C1 {
| public:
|   T V;
|   C1(T v) : V(v) {};
|   
|   class nested {
|   public:
|     T W;
|     nested(T w) : W(w) {};
|   };
|   
| };
| 
| // Template version of operator using nested temptate class
| template <typename T>
| ostream&  operator << (ostream& out,typename C1<T>::nested& c) {return out<<c.W; }

This template is never going to contribute any candidate through the
usual template argument deduction process.  The reason is very simple:
the template parameter "T" appears in nondeducible context.

| // Non template version of operator using nested temptate class
| // ostream&  operator << (ostream& out, C1<float>::nested& c) {return out<<c.W;}

this function does not involve any template deduction, so it will "work".

[...]

|   //operator<< <float> (cout,n);
|   // Never work !! but it should ???

Yes.

-- Gaby
Comment 9 jean-pierre.chevallet 2005-01-10 05:56:29 UTC
(In reply to comment #7)
> You are trying to overload operator<<(std::ostream, float). That 
> can't work, since there is already an overload for that. 
>  
> W. 

I don't think so, I try to define un fonction like this :

operator<<(std::ostream, C1<float>::nested&) with is not an overload of
operator<<(std::ostream, float).

The lesson I learn from all your replies : 
nested class of a template wich have no template
parametrer are difficult to use due to the notion nondeducible context.
I think it is better to avoid non template nested class, or adding a
explicit template parameter to the nested class, like :

template <typename T> class C1 {
public:
  T V;
  C1(T v) : V(v) {};
  
  template <typename U>
  class nested {
  public:
    U W;
    nested(U w) : W(w) {};
  };  
};
// Template version of operator using nested temptate class
template <typename T>
ostream&  operator << (ostream& out,typename C1<T>::nested<T>& c) {return
out<<c.W; }

  C1<float>::nested<float> n(3.5);

  cout<<n<<endl; // Here find the correct instantiation

or not use nested template class at all ........