This works fine. For any type I create (Example):

BoxArray& operator -= (const Box& B);

BoxArray& operator -= (const BoxArray& BA);

BUT when it comes to my literal types like char's and strings and char*'s, I get Ambiguous overloads:

StringArray& operator -= (const std::string& S);

StringArray& operator -= (const StringArray& SA);

I have no clue why! They are the exact same syntax, it works for all my classes, why does it not work for literal types? I've coded them all the exact same so are Literals special when overloading?

Also for my next question, I'm trying to delete values in a vector and it works but I've been using parallel vectors inorder to do lower-case comparisons such as:

void Delete(char CharToDelete, bool CaseSensitive, bool All)
{
    std::vector<char> Temp = ChrA;  //Copy my original/global vector to a temporary vector.
    if (!CaseSensitive)
    {
        CharToDelete = tolower(CharToDelete);

        for (size_t I = 0; I < Temp.size(); I++)
            Temp[I] = tolower(Temp[I]);
    }

    //Compare the temporary type to the one to delete and remove it from the global vector and temp vector to avoid out of range errors.

    for (size_t I = 0; I < Temp.size(); I++)
    {
        if (Temp[I] == CharToDelete)
        {
            ChrA.erase(ChrA.begin() + I);   //Parallel Deletion..
            Temp.erase(Temp.begin() + I);   //Parallel Deletion..
            if (!All)
                break;
            I = 0;
        }
    }
    return;  //Do not return anything since I already removed it from the global vector too.
}

Is there a better way to do this? The underlying types are just vectors. I did typedef vector<char> CharArrayto make it look better.

Finally my last question is to make unique values in an already constructed vector without sorting it.
Currently I use:

vector<char> CharArray;

void MakeUniqueSorted()
{
    sort(CharArray.begin(), CharArray.end());
    CharArray.erase(unique(CharArray.begin(), CharArray.end()), CharArray.end());
}

But I cannot think of a way to do it without using sort. Any help is appreciated.

Edited 4 Years Ago by triumphost

In the future, you should restrict one thread to one question. The discussion becomes difficult to have and to read when there are three separate issues in parallel.

They are the exact same syntax, it works for all my classes, why does it not work for literal types? I've coded them all the exact same so are Literals special when overloading?

Literals are not special when overloading, except that you might not be aware of their original type. A string literal, as in "hello world" has the type const char[]. In order to send it to a function that takes a parameter of type const std::string&, it must first be implicitely converted into a std::string and then bound to the const-reference parameter. If the expected type is const StringArray&, whatever that is, the process is the same (implicit conversion + const-ref binding). To the compiler, if both paths are possible, they are equally good, and thus, it cannot make a decision and tells you instead that the overload is ambiguous.

In your other example, you probably never encountered that issue because there was always a clear favorite choice. In a nutshell, the compiler basically chooses the most direct path (i.e., prefers no conversion at all, or just a reference binding, and will want to avoid multiple conversions), and when there's no clear winner, it forfeits with an "ambiguous overload" error.

So, the error is in the implicit conversions that you allow from a string literal to a StringArray or any other string-like type involved in the overload resolution. In general, you fix this problem by avoiding implicit conversions (disallowing it). Here's how to do it. If you have a constructor like this:

class StringArray {
  // ...
    StringArray(const char* str);
};

Then, that constructor is considered a means to do an implicit conversion from a string literal to a StringArray class, as in StringArray my_str = "hello world";, or in the context of a function parameter. To disallow that, you can either remove the constructor, or better yet, use the keyword explicit to tell the compiler to allow only explicit conversions using that constructor, as in:

class StringArray {
  //...
    explicit StringArray(const char* str);
};

With that, things like the my_str initialization above will not be allowed and would require this explicit syntax instead: StringArray my_str = StringArray("hello world");. This ensures that all conversions into a StringArray object are voluntary (not by accident). And it will also solve your overload ambiguity.

N.B. The only reason the standard std::string class has an implicit conversion from a const char* and thus a string literal is because that standard string class is meant to act as THE built-in replacement for the C-style strings. If it wasn't special like that, it probably would not have been designed with an implicit conversion.

Is there a better way to do this?

Why do you need to copy the vector?

void Delete(char CharToDelete, bool CaseSensitive, bool All)
{
    if (!CaseSensitive)
        CharToDelete = tolower(CharToDelete);

    for (size_t I = 0; I < ChrA.size(); I++)
    {
        if ( ( CaseSensitive ?
            (ChrA[I] == CharToDelete) :
            (tolower(ChrA[I]) == CharToDelete) ) )
        {
            ChrA.erase(ChrA.begin() + I);   //Deletion..
            if (!All)
                break;
            --I;        //note here, you don't need to reset the index to 0.
        }
    }
}

Now, in reality, you shouldn't do the above because it could cause a lot of unnecessary copying of data (every time the vector gets resized by the removal of an element). Instead, you can use two chasing indices to simply repack the vector elements, and then remove only what is superfluous (this is the C++ standard way, take a look at std::remove()). Here is roughly how to do it:

    size_t J = 0;
    for (size_t I = 0; I < ChrA.size(); I++)
    {
        if ( ( CaseSensitive ?
            (ChrA[I] != CharToDelete) :
            (tolower(ChrA[I]) != CharToDelete) ) )
        {
          ChrA[J++] = ChrA[I];  // copy only good characters.
        }
    }
    // then, erase the remaining elements:
    ChrA.erase(ChrA.begin() + J, ChrA.end());

And there is a very similar loop that uses iterators instead (as does the standard remove function). You could also do this using the standard std::remove_if function, if you program a predicate function to check the invalidity of the characters.

But I cannot think of a way to do it without using sort. Any help is appreciated.

Well, without sorting, you certainly cannot do it efficiently. Currently, you do it in O(N logN + N) (with NlogN for sort and N for the unique() function). If you don't sort it, you will basically have to do it in O(N^2), like this:

void MakeUniqueUnsorted1()
{
  typedef vector<char>::iterator iter;
  iter it_out = CharArray.begin();
  for(iter it = CharArray.begin(); it != CharArray.end(); ++it)
    if(std::find(CharArray.begin(),it,*it) == it)
      *(it_out++) = *it;
  CharArray.erase(it_out, CharArray.end());
};

It ain't pretty but it works. This works too:

void MakeUniqueUnsorted2()
{
  typedef vector<char>::iterator iter;
  iter it_end = CharArray.end();
  for(iter it = CharArray.begin(); it != it_end; ++it)
    it_end = std::remove(it + 1, it_end, *it);
  CharArray.erase(it_end, CharArray.end());
};

Edited 4 Years Ago by mike_2000_17: alternative

Comments
Excellent Solution.

I made a temporary variable so my class member variable does not change. For Chars it isn't neccessary but when it comes to 2D arrays of strings, I couldn't think of another way other than to make a temp copy, lowercase it and just match against that.

This was my first time ever encountering a situation where I needed to use explicit. Thank you for the explanation though. I just finished reading up on why it is needed sometimes.

I've never heard of the O(N^ 2) thing before I'll research it before using those functions. Thank you though. Much appreciated and I'll try to keep things to one thread from now on.

This question has already been answered. Start a new discussion instead.