2

Consider the following:

template <typename T>
class testString
{
public:

  typedef T* iterator;

  void insert(iterator aPos, size_t numChars, T aChar);
  testString<T>& insert(size_t aIndex, size_t numChars, T aChar);
};

template <typename T>
void testString<T>::insert( iterator aPos, size_t numChars, T aChar )
{

}

template <typename T>
testString<T>& testString<T>::insert( size_t aIndex, size_t numChars, T aChar )
{
  return *this;
}

int main()
{
  testString<char> c;
  c.insert(0, 10, 'a'); // ambiguous call
  c.insert(1, 10, 'a'); // compiles fine

  return 0;
}

Why am I getting an ambiguous call? Initially, I had a guess (because it is 0, it can be anything), but then I looked at std::string. I looked at the source, and these are the two functions:

void               insert ( iterator p, size_t n, char c );
string&            insert ( size_t pos1, size_t n, char c );

And I tried the same calls with std::string, and it works fine. Now I have no clue why 0 works with std::string and is immediately recognized as a size_t while in my implementation it is not (another guess was type traits, but there is no evidence of it in the source)?

Samaursa
  • 16,527
  • 21
  • 89
  • 160
  • 1
    0 is both an integer and a null pointer. What's the iterator type for string in your implementation? – K-ballo Sep 18 '11 at 00:04
  • This is a picturesque "LOLWUT" in C++. +1. – Thanatos Sep 18 '11 at 00:14
  • @K-ballo: In my case, the iterator type is simply a pointer to type T – Samaursa Sep 18 '11 at 06:12
  • @Samaursa: Not the iterator type for your string implementation, but the iterator type for the `std::string` provided by your implementation. Anyway, the point was that the standard string iterator is not a pointer, you already see why not. – K-ballo Sep 18 '11 at 06:16

2 Answers2

3

Ah, one of those times when you're like "wouldn't it be better if in c++, 0 was not the same as null?"

Anyway, your iterator type is typedef'd to T*, which is char*, and 0 is a fine value for a char*, hence the ambiguity (er, as K-ballo wrote). In std::string, the iterator type won't be a char*, it'll be something fancy like another (templatized) class...

Colin
  • 3,670
  • 1
  • 25
  • 36
  • Any references for the iterator type of std::string? – flight Sep 18 '11 at 00:13
  • the iterator type of `std::string` is `std::string::itertaor`, which is generally a class type with appropriate overloaded operators so it acts like a pointer. – Chris Dodd Sep 18 '11 at 00:22
  • Ah, ok, that makes sense. In EASTL (Electronics Arts version of STL) the `iterator` is also `T*` and will suffer from the same problem. Interesting. Now, is there any way to get around this while keeping the functionality the same to ensure the class is standard compliant (and not adding a fancy iterator)? – Samaursa Sep 18 '11 at 00:45
  • @Samaursa static_cast(0) should resolve the ambiguity. – IronMensan Sep 18 '11 at 02:03
  • @IronMensan: That has to be from the client side, which I would like to avoid. – Samaursa Sep 18 '11 at 04:04
  • @Samaursa You can use template specialization, but that would be bad. If _you_ were the compiler, and someone wrote x.insert(0...), which function would you call? Did the user mean '0 as in null' or '0 as in 0'? – Colin Sep 18 '11 at 23:18
  • @Colin: I dunno, compiler design requires a lot of thought :) My first solution would be to remove the ambiguity by the second argument which is also of the same type. If the ambiguity is successfully removed after looking at the second argument, it compiles. If there is no second argument, then it shouldn't compile. But that is just an off-the-cuff answer, it probably has caveats. – Samaursa Sep 20 '11 at 22:53
1

Since 0 could be treated both as a pointer and an integer, you get the ambiguity.

If you want to distinguish them, my suggestion would be to wrap your T * in a class inside testClass. That is something like:

template <typename T>
class testString
{
public:
  class iterator
  {
  public:
    T *addr;
  }
  void insert(iterator aPos, size_t numChars, T aChar);
  testString<T>& insert(size_t aIndex, size_t numChars, T aChar);
};

If you have noticed, to STL either, you can't send an integer value as iterator, but you must pass an iterator of type whateverclass<whatevertype>::iterator.

Shahbaz
  • 46,337
  • 19
  • 116
  • 182