2015-09-11 2 views
1

Следующий кодНеоднозначность перегрузки с массивом передается как указатель

#include <iostream> 

using namespace std; 

class A {}; 

class B : public A {}; 

class C : public B {}; 

void foo(A *a) { 
    cout << 'A' << endl; 
} 

void foo(B *b) { 
    cout << 'B' << endl; 
} 

int main() { 
    C *c = new C[10]; 

    foo(c); 
} 

компилирует тонкой и печатает «B», как и ожидалось.

Но когда я изменить main() функцию

int main() { 
    C c[10]; 

    foo(c); 
} 

Я получаю ошибку компиляции, говоря

test_arr.cpp: In function 'int main()': 
test_arr.cpp:23:10: error: call of overloaded 'foo(C [10])' is ambiguous 
test_arr.cpp:23:10: note: candidates are: 
test_arr.cpp:11:6: note: void foo(A*) 
test_arr.cpp:15:6: note: void foo(B*) 

Почему это неоднозначное сейчас? Я думал, что массив всегда был просто указателем, поэтому я не вижу разницы.

Edit:

Я просто понял, что весь код, который я была плохая идея, чтобы начать с: Как только вы добавляете элементы данных в классы и доступ к ним в foo функции вы столкнетесь с проблемами , как объясняется here, так как функция foo(B*) не имеет способа узнать, что она фактически имеет дело с объектами C, которые потенциально занимают больше места в памяти. Так что не делай этого!

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

+0

vs2008, я не могу воспроизвести ошибку - и последний тестовый пример печатает «B». – Gombat

+0

Работает ли foo (& c [0])? – Gombat

+4

[Массивы не указатели] (https://stackoverflow.com/questions/1641957/is-array-name-a-pointer-in-c). Откуда взялась эта идея? Кроме того, арифметика указателя, необходимая для доступа к массиву, не будет работать, если вы нарисуете массив 'C' на указатель на' B' (или 'A', если на то пошло); как только 'C' содержит некоторые члены данных,' b [1] 'не даст вам действительный объект, потому что' sizeof (B)! = sizeof (C) '(и это неопределенное поведение до этого также). – Wintermute

ответ

5

Я считаю, что это ошибка gcc. Код компилируется на clang.

Во-первых, оба кандидата жизнеспособны. Из [ко]:

A стандартной последовательность преобразования представляет собой последовательность стандартных преобразований в следующем порядке:
- Ноль или один преобразование из следующего набора: именующие к RValue конверсии, array- to-pointer, и преобразование функции в указатель.
- Ноль или один преобразование из следующего набора: интегральные поощрений, продвижения с плавающей точкой, интегральных преобразования, с плавающей точкой преобразования плавающим интегральными преобразований, преобразования указателей, указатель на преобразование членов и логические преобразования.

Оба вызова к foo будет включать в массиве к-указатель преобразование (от C[10] к C*), а затем преобразование указателя (от C* к любому A* или B*). Это допустимое стандартное преобразование.

Теперь, как мы оцениваем эти преобразования? Занятно, линия от стандарта соответствует точно нашему потребительному случаю, в [over.ics.rank]:

два последовательностей конверсии с одинаковым рангом неразличимы, если не применяется один из следующих правил:
- Если класс в является производным непосредственно или косвенно из класса а и класса с происходит непосредственно или косвенно из B,
      - преобразование C * до B * лучше, чем конверсии C * до A *

У нас есть две последовательности конверсий одного ранга (Конверсия), которые являются жизнеспособными, но один считается лучшим, чем другой. Код должен однозначно отдавать предпочтение foo(B*) за foo(A*). Тот факт, что c объявлен массив, не должен делать его неоднозначным.

1

C c[10] не является полиморфным. Возможно, вы сможете получить указатель на первый элемент в массиве, но это все еще только C.

Вы можете нарезать, если попытаетесь наложить его на B.

C* c = new C(); может быть dynamic_cast до B.

+0

@sehe Я запутался в том, как этот вопрос связан с преобразованиями больше, чем тот факт, что его 'C [10]' на самом деле не является полиморфным типом? –

Смежные вопросы