declaration of ... shadows template parm ...

Jan 3, 2011 at 5:43pm
I cannot figure this one out, nor do I understand how to fix it.

My biginteger class is declared thus:

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
32
33
34
35
36
37
38
39
40
41
42
namespace duthomhas
  {
  ...

  template <typename AllocatorType>
  class biginteger_t
    {
    //------------------------------------------------------------------------
    public: // types
    //------------------------------------------------------------------------

      typedef AllocatorType                 alloc_t;
      typedef typename alloc_t::value_type  value_t;
      typedef std::deque <value_t, alloc_t> array_t;
      typedef typename array_t::size_type   index_t;

      typedef biginteger_t <alloc_t>        biginteger;

    ...

      template <typename CharT, typename TraitsT, typename AllocT>
      std::basic_string <CharT, TraitsT, AllocT>
      basic_string( unsigned radix = 10 ) const
        {
        ...
        }

      std::string  string(  unsigned radix = 10 ) const { return basic_string <char,    std::char_traits <char>    , std::allocator <char>    > ( radix ); }
      std::wstring wstring( unsigned radix = 10 ) const { return basic_string <wchar_t, std::char_traits <wchar_t> , std::allocator <wchar_t> > ( radix ); }

    ...

      template <typename CharT, typename TraitsT, typename AllocT>
      friend
      std::basic_ostream <CharT, TraitsT> &
      operator << (
        std::basic_ostream <CharT, TraitsT> & outs,
        const biginteger_t <AllocT>         & n
        );
    };

  ...
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
  template <typename CharT, typename TraitsT, typename AllocT>
  std::basic_ostream <CharT, TraitsT> &
  operator << (
    std::basic_ostream <CharT, TraitsT> & outs,
    const biginteger_t <AllocT>         & n
    ) {
    std::basic_string <CharT, TraitsT, std::allocator <CharT> >
    s = n.basic_string <CharT, TraitsT, std::allocator <CharT> > ();  // todo: add base conversion based upon stream's fmtflags
    return outs << s;
    }
1
2
  ...
  } // namespace duthomhas 

The compiler complains thusly:

In file included from a.cpp:1:
bignum.hpp: In function 'std::basic_ostream<_CharT, _Traits>& duthomhas::operator<<(std::basic_ostream<_CharT, _Traits>&, const duthomhas::biginteger_t<AllocT>&)':
bignum.hpp:1516: error: expected primary-expression before ',' token
bignum.hpp:1516: error: declaration of 'std::basic_string<CharT, TraitsT, std::allocator<_CharT> > TraitsT'
bignum.hpp:1509: error:  shadows template parm 'class TraitsT'
bignum.hpp:1516: error: invalid declarator before '>' token

I'm not sure exactly where I'm crossing names, but I think it has something to do with allowing the biginteger type to be templated...

I've tried changing all the stream/string template names, and also the name of my basic_string function, but I get the exact same errors.

Where exactly am I going wrong?

Thank you for reading.
Jan 3, 2011 at 6:12pm
LOL, I think I solved it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  template <typename AllocatorType>
  class biginteger_t
    {
    ...

      // What'd'ya know? I moved this inside the class block, with a friend status...
      template <typename CharT, typename TraitsT>
      friend
      std::basic_ostream <CharT, TraitsT> &
      operator << (
        std::basic_ostream <CharT, TraitsT> & outs,
        const biginteger                    & n
        ) {
        // todo: add base conversion based upon stream's fmtflags
        return outs << n.basic_string <CharT, TraitsT, std::allocator <CharT> > ();
        }

    };

Naturally, it compiles swimmingly.

I would like to know, if there is one, an answer to the original problem though... What would I have to do to declare my friend insertion operator outside the class's declaration block?
Jan 3, 2011 at 7:32pm
You've got to be kidding me. I didn't know this "friend defined inside a class" monster. Could you explain what part of the code can see it and use it? Does the code get inlined? What I got from google goes like this: it is defined in the surrounding namespace, but is visible only inside the class.

Anyways, I think what you needed to do in your original code was:
1
2
3
    std::basic_string <CharT, TraitsT, std::allocator <CharT> >
    s = n.template  basic_string <CharT, TraitsT, std::allocator <CharT> > ();  // todo: ...
    return outs << s;

The type of n depends on a template parameter and the compiler assumes that basic_string is a member object that is not a template. Therefore the following < is interpreted as the less-than operator. Inside the class, the type of n is not anymore dependent on the template parameter, in the sense that the declaration of n can be located in the current class template. But I am still learning how those rules work exactly. The explanation I had in my head doesn't cover this case. I just gave you my intuition.

Regards
Last edited on Jan 3, 2011 at 7:34pm
Jan 3, 2011 at 7:47pm
simeonz wrote:
You've got to be kidding me. I didn't know this "friend defined inside a class" monster. Could you explain what part of the code can see it and use it? Does the code get inlined?


Friend functions can be defined inside template declarations, but it will not get istantiated until
a concrete class of the template is actually created.
here is some info from 'templates - the complete guide'


However, an interesting effect occurs when a friend function is defined in a class template
because anything that is only declared in a template isn't a concrete entity until
the template is instantiated. Consider the following example:

1
2
3
4
5
6
template <typename T> 
class Creator { 
    friend void appear() {  // a new function ::appear(), but it doesn't // exist until Creator is instantiated 
    } 
}; 


1
2
Creator<void> miracle;  // ::appear() is created at this point 
Creator<double> oops;   // ERROR: ::appear() is created a second time!  

In this example, two different instantiations create two identical definitions—a direct violation of the ODR (see Appendix A).

We must therefore make sure the template parameters of the class template
appear in the type of any friend function defined in that template
(unless we want to prevent more than one instantiation of a class template
in a particular file, but this is rather unlikely).
Let's apply this to a variation of our previous example:

1
2
3
4
5
6
template <typename T> 
class Creator { 
    friend void feed(Creator<T>*){  // every T generates a different // function ::feed() 
    } 
}; 


1
2
Creator<void> one;     // generates ::feed(Creator<void>*) 
Creator<double> two;   // generates ::feed(Creator<double>*)  

In this example, every instantiation of Creator generates a different function.
Note that even though these functions are generated as part of the instantiation of a template,
the functions themselves are ordinary functions, not instances of a template.

Also note that because the body of these functions is defined inside a class
definition, they are implicitly inline.
Hence, it is not an error for the same function to be generated in two different translation units.
Jan 3, 2011 at 8:00pm
Woah, that is awesomely cool! I didn't know about the template qualifier and how it worked like that!

The insertion operator really does not need to be a friend of the class, so I am now using your excellent solution:

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
...

namespace duthomhas
  {

  ...

  template <typename AllocatorType>
  class biginteger_t
    {
    ...
    };

  ...

  template <typename AllocT, typename CharT, typename TraitsT>
  std::basic_ostream <CharT, TraitsT> &
  operator << (
    std::basic_ostream <CharT, TraitsT> & outs,
    const biginteger_t <AllocT>         & n
    ) {
    return outs << n.template basic_string <CharT, TraitsT, std::allocator <CharT> > ();  // todo: ...
    }

  } // namespace duthomhas

...

It is legal to define a friend function that way so long as certain conditions are met (mainly, that the class is non-local).

Thank you!
Jan 4, 2011 at 1:15am
Well guys, I am always happy to learn new aspects. On the other hand, C++ classes are such mix of interface and implementation. Thanks for the info.
Last edited on Jan 4, 2011 at 1:16am
Topic archived. No new replies allowed.