0

I have a template class in a header that fails to compile, but when I try to make a minimal show case, it compiles fine. I'm trying to make a class that can be constructed from an array, deducing the length automatically. The error is "reference to a zero-sized array is illegal", which makes no sense, as it should automatically deduce the length via the template.
Related Code:

template<class _Elem, bool _Null=true, bool _Trunc=false, class _Traits = std::char_traits<char>>
class basic_estring {
public:
    typedef typename _Traits::pos_type size_type;
    typedef typename _Elem value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type &reference;
    typedef const value_type &const_reference;
    static const size_type npos;

    basic_estring(const_pointer ptr, size_type capacity=npos, size_type used=npos);
    template<int capacity> basic_estring(value_type (&data)[capacity], size_type used=npos);
    // several hundred lines of other declarations
};
template<class _Elem, bool _Null, bool _Trunc, class _Traits>
basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(
    typename basic_estring<_Elem,_Null,_Trunc,_Traits>::const_pointer ptr, 
    typename basic_estring<_Elem,_Null,_Trunc,_Traits>::size_type capacity, 
    typename basic_estring<_Elem,_Null,_Trunc,_Traits>::size_type used)
{} //This constructor has no problems (and all the others too)

template<class _Elem, bool _Null, bool _Trunc, class _Traits> template<int capacity> 
basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(
    typename basic_estring<_Elem,_Null,_Trunc,_Traits>::value_type (&data)[capacity], //LINE 218
    typename basic_estring<_Elem,_Null,_Trunc,_Traits>::size_type used)
{} //LINE 228
//several hundred lines of definitions

And the error from MSVC C++ 2010:

1>/*snip*/\estring.h(218): error C2265: 'abstract declarator' : reference to a zero-sized array is illegal
1>/*snip*/\estring.h(228): error C2244: 'basic_estring<_Elem,_Null,_Trunc,_Traits>::{ctor}' : unable to match function definition to an existing declaration
1>  definition
1>  'basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring<_Elem,_Null,_Trunc,_Traits>::value_type (&)[1],basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring<_Elem,_Null,_Trunc,_Traits>::size_type)'
1>  existing declarations
1>  'basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(const basic_estring<_Elem,_Null,_Trunc,_Traits> &&)'
1>  'basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(const basic_estring<_Elem,_Null,_Trunc,_Traits> &)'
1>  'basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(const std::basic_string<_Elem,_Traits,Alloc> &)'
1>  'basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(_Elem (&)[capacity],_Traits::pos_type)'
1>  'basic_estring<_Elem,_Null,_Trunc,_Traits>::basic_estring(const _Elem *,_Traits::pos_type,_Traits::pos_type)'

Anyone know what I'm doing wrong? I'm usually pretty good about solving my own problems, but this has me stumped. I rewrote it from the other constructors, and that solved nothing.
[Edit] The error is still there when the non-prototype code in this project is

int main() {
    return 0;
}

(including no function definitions) So, it isn't an instantiation problem as far as I can tell. While I was commenting out this code, I kept commenting out code from inside the class too, and discovered that if I commented out the member function prototype capacity, the error goes away. My class does not inherit from any other classes.

void resize( size_type Count );
void resize( size_type Count, value_type Ch );
size_type capacity( ) const;  //if this is commented, the error goes away.
void reserve( size_type Count );

Now I'm totally stumped. [/Edit]

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • First, you should avoid prefixing your symbols with an underscore, unless you really know what you're doing. See: http://stackoverflow.com/questions/228783 – paercebal Jul 22 '11 at 23:24
  • Second, did you try to inline the constructor inside the class, to see what happens? – paercebal Jul 22 '11 at 23:25
  • Can you show the calling code? The problem with minimal examples is that functions in templated classes don't even get instantiated if they're not used, so you might be missing errors. Try to make your minimal example use all the class member functions. – Kerrek SB Jul 23 '11 at 00:08
  • @paercebal I was duplicating some of std::basic_string's functionality, so I duplicated a lot of the parameter names too. I've removed the underscores though. – Mooing Duck Jul 28 '11 at 17:59
  • @Kerrek SB, there is no calling code yet, unless it's inside one of the member function definitions of this class. I couldn't find anything like that though. – Mooing Duck Jul 28 '11 at 18:00

2 Answers2

1

It seems somewhere in your code you are passing in a zero-sized array as an argument to your constructor, causing the compiler to attempt to instantiate a template for the matching basic_estring constructor function signature that would define capacity as 0, and in-turn pass a reference to a zero-sized array to your constructor. In other words the problem is not your constructor declaration, but is somewhere else in your code where you are attempting to instantiate a specific version of the constructor template.

For instance, this code will compile just fine:

#include <iostream>

template<typename T>
class A
{
    T* array;

    public:
        template<int capacity>
        A(T (&data)[capacity]) { std::cout << capacity << std::endl; }
};

int main()
{
        int array[5];
        A<int> b(array);

        return 0;
}

On the other-hand changing the declaration of array to

int main()
{
    int array[] = {};
    A<int> b(array);

    return 0;
}

No longer compiles. It can't deduce the template value for capacity from the input parameters. In fact, gcc can't seem to correctly identify the argument type either, as the error comes out as

prog.cpp: In function ‘int main()’:
prog.cpp:22: error: no matching function for call to ‘A<int>::A(int [0])’
prog.cpp:5: note: candidates are: A<int>::A(const A<int>&)

meaning that gcc is looking for some type of single-argument constructor that can be used for type conversion, with the default compiler-created copy constructor being the only available candidate since I never declared any other constructors with single arguments.

So your constructor template declaration/definition itself is fine, but if you are instantiating it with a zero-length array, then the argument type is incorrect, and the template can't be properly instantiated. The first place I'd look for your error is where your basic_estring is being instantiated, rather than wondering what is wrong with the constructor itself.

Jason
  • 31,834
  • 7
  • 59
  • 78
  • That was my first thought too. I commented out all the code that wasn't part of the class, and the error still occurred. However, I just discovered if I comment out the capacity() prototype, the error goes away. – Mooing Duck Jul 28 '11 at 17:57
1

I finally figured it out. The constructor with a template type capacity was failing when the class also had a member function capacity. Whoops. Problem goes away when I rename with template type to Cap

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158