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