Почему эта двусмысленность здесь?

ILMerge делает именно то, что вы хотите.

35
задан Kirill V. Lyadvinsky 11 September 2013 в 05:26
поделиться

6 ответов

На самом деле все довольно просто. Для t [1] разрешение перегрузки имеет следующие кандидаты:

Кандидат 1 (встроенный: 13.6 / 13) (T - произвольный тип объекта):

  • Список параметров: (T *, ptrdiff_t)

Кандидат 2 (ваш оператор)

  • Список параметров: (TData &, что-то без знака)

Список аргументов задается 13.3.1.2/6 :

Набор функций-кандидатов для разрешения перегрузки представляет собой объединение кандидатов в члены, кандидатов, не являющихся членами, и встроенных кандидатов. Список аргументов содержит все операнды оператора.

  • Список аргументов: (TData , int)

Вы видите, что первый аргумент точно соответствует первому параметру кандидата 2. Но для этого требуется определенное пользователем преобразование для первого параметра кандидата 1. Таким образом, для первого параметра побеждает второй кандидат.

Вы также видите, что исход второй позиции зависит. Сделаем некоторые предположения и посмотрим, что мы получим:

  1. ptrdiff_t is int : первый кандидат побеждает, потому что он имеет точное совпадение, а второй кандидат требует интегрального преобразования.
  2. ptrdiff_t is long : ни один из кандидатов не выигрывает, потому что оба требуют интегрального преобразования.

Теперь 13.3.3 / 1 говорит

Пусть ICSi (F) обозначает последовательность неявного преобразования, которая преобразует i-й аргумент в списке в тип i-го параметра жизнеспособная функция F.

Жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов i, ICSi (F1) не является худшей последовательностью преобразования, чем ICSi (F2), а затем ... для некоторого аргумента j , ICSj (F1) - лучшая последовательность преобразования, чем ICSj (F2), или, если не это ...

Для нашего первого предположения мы не получаем общего победителя, потому что кандидат 2 побеждает по первому параметру, Кандидат 1 побеждает по второму параметру. Я называю это крест-накрест . Для нашего второго предположения кандидат 2 в целом выигрывает, потому что ни один параметр не имеет худшего преобразования, но первый параметр имеет лучшее преобразование.

Для первого предположения не имеет значения, что интегральное преобразование (int в беззнаковое) во втором параметре является меньшим злом, чем определяемое пользователем преобразование другого кандидата в первом параметре. При перекрестке правила грубые.


Этот последний пункт может все еще сбить вас с толку из-за всей суеты, поэтому давайте приведем пример.

void f(int, int) { }
void f(long, char) { }

int main() { f(0, 'a'); }

Это дает вам такое же сбивающее с толку предупреждение GCC (которое, я помню, на самом деле сбивало меня с толку, когда я впервые получил его несколько лет назад), потому что 0 преобразуется в long хуже, чем 'a' в int - но вы получаете двусмысленность , потому что вы находитесь в перекрестной ситуации.

32
ответ дан 27 November 2019 в 07:19
поделиться

Я не знаю точного ответа, но ...

Из-за этого оператора:

operator ptr_t & () { return data; }

уже существует встроенный [] оператор (подписка на массив), который принимает size_t в качестве индекса. Итак, у нас есть два [] оператора, встроенный и определенный вами. Бут принимает size_t , так что это, вероятно, считается незаконной перегрузкой.

// ИЗМЕНИТЬ
это должно работать так, как вы планировали

template<typename ptr_t>
struct TData
{
    ptr_t data;
    operator ptr_t & () { return data; }
};
0
ответ дан 27 November 2019 в 07:19
поделиться

Я попытался показать двух кандидатов на выражение t [1] [1]. Оба они имеют одинаковый РАНГ (ПРЕОБРАЗОВАНИЕ). Отсюда двусмысленность

. Я думаю, что загвоздка в том, что встроенный оператор [] согласно 13.6 / 13 определен как

T& operator[](T*, ptrdiff_t);

В моей системе ptrdiff_t определен как 'int' (объясняет ли это поведение x64?)

template<typename ptr_t> 
struct TData 
{ 
    typedef typename boost::remove_extent<ptr_t>::type value_type; 
    ptr_t data; 

    value_type & operator [] ( size_t id ) { return data[id]; } 
    operator ptr_t & () { return data; } 
}; 

typedef float (&ATYPE) [100][100];

int main( int argc, char ** argv ) 
{ 
    TData<float[100][100]> t;    

    t[size_t(1)][size_t(1)] = 5; // note the cast. This works now. No ambiguity as operator[] is preferred over built-in operator

    t[1][1] = 5;                 // error, as per the logic given below for Candidate 1 and Candidate 2

    // Candidate 1 (CONVERSION rank)
    // User defined conversion from 'TData' to float array
    (t.operator[](1))[1] = 5;

    // Candidate 2 (CONVERSION rank)
    // User defined conversion from 'TData' to ATYPE
    (t.operator ATYPE())[1][1] = 6;

    return 0; 
}

РЕДАКТИРОВАТЬ:

Вот что я думаю:

Для кандидата 1 (оператор []) последовательность преобразования S1 равна Пользовательское преобразование - стандартное преобразование (int в size_t)

Для кандидата 2 последовательность преобразования S2 имеет вид Пользовательское преобразование -> int в ptrdiff_t (для первого аргумента) -> int в ptrdiff_t (для второго аргумента)

Последовательность преобразования S1 является подмножеством S2 и должна быть лучше. Но вот в чем загвоздка ...

Здесь должна помочь приведенная ниже цитата из Стандарта.

$ 13.3.3.2 / 3 состояния - Стандарт последовательность преобразования S1 лучше последовательность преобразования, чем стандартная последовательность преобразования S2, если - S1 является собственная подпоследовательность S2 (сравнивая последовательности преобразования в каноническая форма, определенная в 13.3.3.1.1, исключая любое преобразование Lvalue; последовательность преобразования идентичности считается подпоследовательностью любого неидентификационная последовательность преобразования) или, если не то ...

$ 13.3.3.2 заявляет - "Определяется пользователем последовательность преобразования U1 лучше последовательность преобразования, чем другая определяемая пользователем последовательность преобразования U2, если они содержат один и тот же пользовательский функция преобразования или конструктор и если второе стандартное преобразование последовательность U1 лучше, чем вторая стандартная последовательность преобразования U2.«

Здесь первая часть условия и« , если они содержат одну и ту же определяемую пользователем функцию преобразования или конструктор », не выполняется. Таким образом, даже если вторая часть условия и» , если вторая стандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2. "хорошо, ни S1, ни S2 не предпочтительнее другого.

Вот почему сообщение об ошибке gcc phantom " ISO C ++ говорит, что они неоднозначны, хотя худшее преобразование для первого - лучше худшего преобразования для второго »

Этим хорошо объясняется неоднозначность ИМХО

0
ответ дан 27 November 2019 в 07:19
поделиться

С выражением:

t[1][1] = 5;

Компилятор должен сосредоточиться на левой части, чтобы определить, что там происходит, поэтому = 5; игнорируется, пока не будет разрешено lhs . Оставляя нас с выражением: t [1] [1] , которое представляет две операции, причем вторая работает с результатом первой, поэтому компилятор должен учитывать только первую часть выражение: t [1] . Фактический тип - (TData &) [(int)]

Вызов не соответствует точно ни одной функции, так как оператор [] для TData определяется как принимающий аргумент size_t , поэтому для его использования компилятор должен преобразовать 1 из int в size_t с неявным преобразованием. Это первый выбор. Теперь другой возможный путь - применение определенного пользователем преобразования для преобразования TData в float [100] [100] .

Преобразование int в size_t является интегральным преобразованием и имеет рейтинг Преобразование в таблице 9 стандарта, как и пользовательское преобразование из TData в float [100] [100] преобразование в соответствии с §13.3.3.1.2 / 4. Преобразование из float [100] [100] & в float (*) [100] оценивается как точное совпадение в таблице 9.Компилятору не разрешается выбирать из этих двух последовательностей преобразования.

Q1 : Не все компиляторы одинаково придерживаются стандарта. Довольно часто обнаруживается, что в некоторых конкретных случаях компилятор будет работать иначе, чем другие. В этом случае разработчики g ++ решили пожаловаться на стандарт, не позволяющий компилятору выбирать, в то время как разработчики Intel, вероятно, просто молча применили свое предпочтительное преобразование.

Q2 : Когда вы изменяете подпись определяемого пользователем оператора [] , аргумент точно соответствует переданному типу. t [1] идеально подходит для t.operator [] (1) без каких-либо преобразований, поэтому компилятор должен следовать по этому пути.

13
ответ дан 27 November 2019 в 07:19
поделиться

Разрешение перегрузки - это головная боль. Но поскольку вы наткнулись на исправление (исключить преобразование индексного операнда в operator[]), которое слишком специфично для примера (литералы имеют тип int, но большинство переменных, которые вы будете использовать, не являются таковыми), возможно, вы можете обобщить его:

template< typename IT>
typename boost::enable_if< typename boost::is_integral< IT >::type, value_type & >::type
operator [] ( IT id ) { return data[id]; }

К сожалению, я не могу проверить это, поскольку GCC 4.2.1 и 4.5 принимают ваш пример без претензий под --pedantic. Что действительно поднимает вопрос о том, является ли это ошибкой компилятора или нет.

Также, как только я устранил зависимость от Boost, пример прошел Comeau.

0
ответ дан 27 November 2019 в 07:19
поделиться

Мне кажется, что с

t[1][1] = 5;

компилятор должен выбирать между.

value_type & operator [] ( size_t id ) { return data[id]; }

который будет соответствовать, если литерал int будет преобразован в size_t, или

operator ptr_t & () { return data; }

последующей обычной индексацией массива, в этом случае тип индекса точно совпадает.


Что касается ошибки, похоже, что GCC как расширение компилятора хочет выбрать первую перегрузку за вас, а вы компилируете с флагом -pedantic и/или -Werror, который заставляет его придерживаться слов стандарта.

(Я не в -pedantic настроении, поэтому никаких цитат из стандарта, особенно на эту тему)

.
0
ответ дан 27 November 2019 в 07:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: