template <typename T>
class v3 {
private:
T _a[3];
public:
T & operator [] (unsigned int i) { return _a[i]; }
const T & operator [] (unsigned int i) const { return _a[i]; }
operator T * () { return _a; }
operator const T * () const { return _a; }
v3() {
_a[0] = 0; // works
_a[1] = 0;
_a[2] = 0;
}
v3(const v3<T> & v) {
_a[0] = v[0]; // Error 1 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
_a[1] = v[1]; // Error 2 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
_a[2] = v[2]; // Error 3 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
}
};
int main(int argc, char ** argv)
{
v3<float> v1;
v3<float> v2(v1);
return 0;
}
С++ error: operator []: 2 перегрузки имеют аналогичные преобразования
Ответ 1
Если вы прочтете остальную часть сообщения об ошибке (в окне вывода), он станет немного понятнее:
1> could be 'const float &v3<T>::operator [](unsigned int) const'
1> with
1> [
1> T=float
1> ]
1> or 'built-in C++ operator[(const float *, int)'
1> while trying to match the argument list '(const v3<T>, int)'
1> with
1> [
1> T=float
1> ]
Компилятор не может решить, следует ли использовать перегруженный operator[]
или встроенный operator[]
в const T*
, который он может получить с помощью следующей функции преобразования:
operator const T * () const { return _a; }
Оба из них являются потенциально допустимыми интерпретациями строк нарушения:
v.operator float*()[0]
v.operator[](0)
Вы можете устранить двусмысленность, явно указав целые индексы на неподписанные, чтобы преобразование не требовалось:
_a[0] = v[static_cast<unsigned int>(0)];
или изменив ваш перегруженный operator[]
, чтобы взять int
вместо unsigned int
или удалив operator T*() const
(и, возможно, неконстантную версию тоже, для полноты).
Ответ 2
Проще говоря: компилятор не знает, следует ли преобразовать v
в const float*
, а затем использовать operator[]
для указателя или преобразовать 0
в unsigned int
, а затем использовать operator[]
для const v3
.
Исправлено, вероятно, удалить operator[]
. Я не могу думать ни о чем, что дает вам, что оператор преобразования в T * еще не существует. Если бы вы планировали установить проверку границ в operator[]
, я бы сказал, замените операторы преобразования функциями getPointer
(так как в общем случае вы не хотите неявно преобразовывать безопасную вещь в небезопасную вещь), или делать то, что std::vector
делает, что пользователи получают указатель с &v[0]
.
Другим изменением, которое позволяет скомпилировать его, является изменение operator[]
для принятия параметра int
вместо unsigned int
. Затем в вашем коде компилятор недвусмысленно выбирает интерпретацию без преобразования. Согласно моему компилятору, до сих пор нет двусмысленности даже при использовании индекса без знака. Что приятно.
Ответ 3
Когда компилятор компилирует следующие
v[0]
он должен учитывать две возможные интерпретации
v.operator T*()[0] // built-in []
v.operator[](0) // overloaded []
Ни один из кандидатов не лучше другого, потому что каждый из них требует преобразования. Первый вариант требует пользовательского преобразования от v3<T>
до T*
. Второй вариант требует стандартного преобразования из int
(0
is int
) в unsigned int
, поскольку для вашего перегруженного []
требуется аргумент unsigned int
. Это делает эти кандидаты несравненными (никоим образом не лучше с помощью правил С++) и, таким образом, делает вызов беспредельным.
Если вы вызываете оператор как
v[0U]
неопределенность исчезнет (поскольку 0U
уже является unsigned int
), и ваш перегруженный []
будет выбран. Кроме того, вы можете объявить перегруженный []
аргументом int
. Или вы можете полностью удалить оператор преобразования. Или сделайте что-нибудь еще, чтобы устранить двусмысленность - вы решаете.
Ответ 4
Это ваш оператор преобразования типа, который является виновником. v преобразуется в указатель float. Теперь существует два оператора [], один из которых - встроенный оператор индексирования для float, а другой - тот, который вы определили на v, который должен выбрать язык, поэтому это двусмысленность в соответствии с ISO.
Ответ 5
Помните, что класс является другом:
v3(const v3<T> & v)
{
_a[0] = v._a[0];
_a[1] = v._a[1];
_a[2] = v._a[2];
}
При копировании чего-то такого же типа вы уже знакомы с деталями реализации. Таким образом, не является проблемой получить доступ к реализации напрямую, если это уместно. Таким образом, из конструктора вы можете получить доступ к объекту, который вы копируете напрямую, и увидеть его член '_a'.
Если вы хотите узнать исходную проблему:
Литерал '1' в контексте 'v [1]' является целым числом (это синоним знакового целого). Таким образом, для использования оператора [] требуется технически компилятор для вставки преобразования из int в unisgned. Другой альтернативой является использование оператора *() для получения указателя на внутренний объект, а затем с помощью оператора [] на указателе. Компилятор не может сделать этот выбор и ошибку:
Параметры компилятора:
_a[1] = v[1];
// Options 1:
_a[1] = v.operator[]((unsigned int)1);
// Options 2:
_a[1] = v.operator*()[1];
Чтобы сделать его небезразличным, вы можете использовать неподписанный литерал;
_a[1] = v[1u];
В долгосрочной перспективе, возможно, стоит сделать это проще для пользователя.
Преобразуйте оператор [] для использования int, а не unsigned int, тогда вы получите точные соответствия, когда целочисленные литералы (или вы можете иметь два набора операторов []. Один, который использует int и тот, который использует unsigned int).
Ответ 6
Я не видел этого, пока Джеймс Макнеллис не опубликовал полное сообщение об ошибке, но двусмысленность не существует между двумя функциями v3::operator[]()
, как представляется.
Вместо этого, поскольку нет точного соответствия между типами аргументов, компилятор не может решить, следует ли:
a) Используйте v3::operator[](unsigned int) const
, тем самым преобразовывая аргумент int в unsigned или
b) Используйте преобразование v3::operator const T*() const
, за которым следует встроенный оператор индексирования массива.
Вы можете избежать этого, указав оператор [] аргументы int, а не unsigned ints. Но лучшим решением было бы избежать неявного преобразования в T * и вместо этого предоставить функцию Data(), которая сделала это явно.
Ответ 7
У меня была эта же проблема: я решил, что это просто делает оператор typecast явным.
Ответ 8
Версия const
ничего не изменяет. Версия non const
позволяет назначать вещи с использованием нотации массива (v[3] = 0.5;
).