-3

I have problem, because in the class I want to have multiple operator[] for different types of variables.

 type operator[](const std::string val);
 //type operator[](const long int val);
 type operator[](const double val);
 //type operator[](const char val);

But this way is wrong, because compiler returns error of multiple declaration (char, int and double is "the same" variable). Program is working only if 2 positions was commented. But if I used the operator[] with letter 'a', it uses the operator[](double) ('a' becomes 97.000000).

Does somebody have an idea to solve it?

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • slow down man, a [mcve] along with actual error you're getting would hopefully be more expressive than a text explanation. – George Jan 05 '18 at 00:49
  • 1
    Looks to me like the code you've posted compiles just fine (see https://ideone.com/ZEi2Sw). Can you show us the code that's actually causing the error? – scohe001 Jan 05 '18 at 00:51
  • @0xDEADBEEF Of course you can, although in fairness I can't think of a legitimate reason to do so. – George Jan 05 '18 at 00:52
  • 1
    @0xDEADBEEF why do you assume the subscript operator for his class is being used to access an index? What if he has a map-type structure? Or if the subscript operator isn't being used for accessing at all? – scohe001 Jan 05 '18 at 00:53
  • 2
    @0xDEADBEEF Pardon me for assuming you're assuming :) however, if you're not thinking he's using the operator for indexing, I don't quite understand your comment. – scohe001 Jan 05 '18 at 00:59
  • 1
    There's no multiple declaration error . Maybe you are talking about an ambiguity error when attempting to *call* the function with a value that's not one of the listed options, but is convertible to several – M.M Jan 05 '18 at 01:00
  • 2
    @0xDEADBEEF I don't really see their relevance, those "problems" aren't exclusive to `operator[]` args. – George Jan 05 '18 at 01:00

1 Answers1

0

At first some critics: Your exposed sample code is not an MCVE and leaves much room for speculation. (This might be one reason for all these down-votes.)

However, as this raised my attention I want to add my 2 cents.

I fully agree with scohe001 and M.M – the exposed declarations are not a problem.

It becomes a problem if the operator[]() is used with an argument of not exact matching type.

Demonstration:

#include <iostream>
#include <set>
#include <sstream>
#include <string>

// trying to resemble the OP

template <typename VALUE>
class MultiAccessorT {
  public:
    typedef VALUE Value;
    typedef std::set<Value> Table;
  private:
    Table &_tbl;
  public:
    MultiAccessorT(Table &tbl): _tbl(tbl) { }

    typename Table::iterator operator[](const std::string &str)
    {
      std::cout << "using operator[](const std::string&) ";
      std::istringstream cvt(str);
      Value value;
      return cvt >> value ? _tbl.find(value) : _tbl.end();
    }

    typename Table::iterator operator[](long value)
    {
      std::cout << "using operator[](long) ";
      return _tbl.find((Value)value);
    }

    typename Table::iterator operator[](double value)
    {
      std::cout << "using operator[](double) ";
      return _tbl.find((Value)value);
    }

    typename Table::iterator operator[](char value)
    {
      std::cout << "using operator[](char) ";
      return _tbl.find((Value)value);
    }
};

// checking out

int main()
{
  // build some sample data
  std::set<unsigned> tbl = { 0, 2, 4, 6, 8, 10, 12, 14 };
  // template instance and instance
  MultiAccessorT<unsigned> acc(tbl);
  // test the operators
  std::cout << "MultiAccessorT::operator[](const string&): ";
  std::cout << (acc[std::string("6")] != tbl.end() ? "found." : "missed!") << std::endl;
  std::cout << "MultiAccessorT::operator[](long): ";
  std::cout << (acc[6L] != tbl.end() ? "found." : "missed!") << std::endl;
  std::cout << "MultiAccessorT::operator[](double): ";
  std::cout << (acc[6.0] != tbl.end() ? "found." : "missed!") << std::endl;
  std::cout << "MultiAccessorT::operator[](char): ";
  std::cout << (acc['\6'] != tbl.end() ? "found." : "missed!") << std::endl;
  // done
  return 0;
}

I compiled and tested it with g++ on cygwin:

$ g++ --version
g++ (GCC) 6.4.0

$ g++ -std=c++11 -o test-polymorphy test-polymorphy.cc 

$ ./test-polymorphy
MultiAccessorT::operator[](const string&): using operator[](const std::string&) found.
MultiAccessorT::operator[](long): using operator[](long) found.
MultiAccessorT::operator[](double): using operator[](double) found.
MultiAccessorT::operator[](char): using operator[](char) found.

$

To prevent ambiguities (which could break the code), I carefully provided matching arguments to the MultiAccessorT::operator[]() calls:

  • std::string("6") for operator[](const std::string&)
  • 6L for operator[](long)
  • 6.0 for operator[](double)
  • '\6' for operator[](char)
    The backslash encodes an octal number to achieve a char with value 6 which is actually not printable.

To get back to the assumption of ambiguity problems I tried some counter examples:

  1. This works:

    std::cout << "using a constant string \"6\" (type const char[2]) ";
    std::cout << (acc["6"] != tbl.end() ? "found." : "missed!") << std::endl;

    Output was:

    using a constant string "6" (type const char[2]) using operator[](const std::string&) found.
  2. This fails:

    std::cout << "using an int constant 6";
    std::cout << (acc[6] != tbl.end() ? "found." : "missed!") << std::endl;

    The g++ complained:

    test-polymorphy.cc: In function 'int main()':
    test-polymorphy.cc:66:24: error: ambiguous overload for 'operator[]' (operand types are 'MultiAccessorT' and 'int')
       std::cout << (acc[6] != tbl.end() ? "found." : "missed!") << std::endl;
                        ^
    test-polymorphy.cc:26:34: note: candidate: typename MultiAccessorT::Table::iterator MultiAccessorT::operator[](long int) [with VALUE = unsigned int; typename MultiAccessorT::Table::iterator = std::_Rb_tree_const_iterator]
         typename Table::iterator operator[](long value)
                                  ^~~~~~~~
    test-polymorphy.cc:32:34: note: candidate: typename MultiAccessorT::Table::iterator MultiAccessorT::operator[](double) [with VALUE = unsigned int; typename MultiAccessorT::Table::iterator = std::_Rb_tree_const_iterator]
         typename Table::iterator operator[](double value)
                                  ^~~~~~~~
    test-polymorphy.cc:38:34: note: candidate: typename MultiAccessorT::Table::iterator MultiAccessorT::operator[](char) [with VALUE = unsigned int; typename MultiAccessorT::Table::iterator = std::_Rb_tree_const_iterator]
         typename Table::iterator operator[](char value)
                                  ^~~~~~~~
    

    Here we are. As there is no exact matching operator[](int), the compiler looks for possible operators which can be used if an implicit conversion would be applied to int. As the above example shows, there are actually three candidates – an ambiguity.

Back to the first counter example. I remember that I once ran into a terrible issue concerning this.

To illustrate this I will introduce yet another operator[]():

    // This resembles my personal "hair-killer":
    typename Table::iterator operator[](bool value)
    {
      std::cout << "using operator[](bool) ";
      return value ? _tbl.begin() : _tbl.end();
    }

and compile and run again:

$ g++ -std=c++11 -o test-polymorphy test-polymorphy.cc 

$ ./test-polymorphy
MultiAccessorT::operator[](const string&): using operator[](const std::string&) found.
MultiAccessorT::operator[](long): using operator[](long) found.
MultiAccessorT::operator[](double): using operator[](double) found.
MultiAccessorT::operator[](char): using operator[](char) found.
using a constant string "6" (type const char[2]) using operator[](bool) found.

$

Huh, what!? using operator[](bool)?

First, there is no complained ambiguity. Second, it prefers bool over std::string? How comes?

This is really defined behavior according to C++ standard. bool is a primary type – class std::string is not. The string constant "6" is compiled to data of type const char[2] – an array. Arrays may not be passed as arguments – they implicitly decay to a pointer in this case i.e. it becomes a const char*. Now, the compiler checks the available operators (in the first round with primary argument types) and there is only one matching: operator[](bool). The other matching operator[](const std::string&) is even not recognized as it does not start the second round (with extended search including programmed types as well) after having found an unambiguous "hit" in the first.

This is something which happened to me some years ago. At first, I even didn't recognize it but after some time of testing I realized that some detail was somehow wrong. Digging deeper I finally found out exactly such an issue. (This knocked me very hard that time – and probably is responsible that I lost some of my hair.)

Last but not least, I have uploaded the final source code on ideone.com.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56