2010-01-05 3 views
7

Рассмотрим следующий код:C++ явные конструкторы и итераторы

В случае, если выше компиляции? Я чувствую, что это не должно, из-за того, что конструктор отмечен explicit.

Microsoft Visual C++ соглашается, что дает сообщение об ошибке ясное: cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'

Однако, используя Comeau's online compiler, код компилируется успешно.

Что именно?

Редактировать:

Интересно, что изменение vector к set (после добавления к operator <) вызывает оба составителям давать ошибку.

Однако изменение vector<int> к map<int, int> и vector<A> к map<A, A> вызывает как компилятор принять код!

+0

Непосредственно ответил здесь: http://stackoverflow.com/questions/1943228/implicit-constructor-conversion-works-on-explicit-vectorvector-only-sometimes –

+1

Я просто написал ответ здесь, основываясь на этом вопросе + ответе, но это действительно не применяется, поскольку 'std :: vector :: iterator' не является интегральным типом, и конструктор заполнения не вызывается. Поэтому я удалил его. Я думаю, что все, что я могу сказать, это то, что стандарт не запрещает * явное преобразование в любом конструкторе контейнера. – Potatoswatter

+0

VS 2010 Beta ведет себя как Comeau и gcc. –

ответ

3

Я просмотрел STC-реализацию GCC и должен иметь подобное поведение. Вот почему.

  • Элементы vector инициализируются общий шаблон функции, которая принимает любые два типа X и V и называет new(p) X(v) где v является V (я перефразирую немного). Это позволяет осуществлять явное преобразование.
  • Элементы set или map инициализируются частной функции члена _tree<T,…>, которое специфически ожидает T const & быть принят. Эта функция-член не шаблон (за быть членом шаблона), так что если начальная значение не может быть неявно преобразовано в T, вызов завершается с ошибкой. (Снова я упрощаю код.)

Стандарт не требует, чтобы явная операция преобразования или что неявное преобразование не работает при инициализации контейнера с диапазоном. Он просто говорит, что диапазон копируется в контейнер. Определенно двусмысленный для вашей цели.

Удивительная такая двусмысленность существует, учитывая, как они уже усовершенствовали стандарт с учетом таких проблем, как the one I had пару недель назад.

1

Я думаю, это будет зависеть от того, как std::vector<A> As(Iterator,Iterator) реализован в вашей конкретной реализации STL.

+0

Поведение стандартной библиотеки не должно зависеть от реализации. – 2010-01-05 00:40:21

+1

Опять же, есть места (очень редко), где стандарт может быть интерпретирован двумя неэквивалентными способами. –

0

Этот код не компилируется в Комео: сообщение

class Foo 
{ 
public: 
explicit Foo(int bar) 
{ 
} 
}; 

class Bar 
{ 
void DoStuff(Foo foo){ 

} 
void DoStuff2() 
{ 
    DoStuff(4); 
} 
}; 

Ошибки:

"ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int" 
      to "Foo" 
    DoStuff(4); 
      ^

1 error detected in the compilation of "ComeauTest.c". 

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

EDIT Это, однако, составляет:

Foo foo = (Foo)5; 

который является явным преобразованием, так что все нормально. Мое предположение, что класс векселя Коуа делает явное приведение в конструкторе где-то, где нет библиотеки Microsoft.

Больше явных конструкторами - http://www.glenmccl.com/tip_023.htm

0

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

+0

Если конструктор не используется, как еще мы переводим из int в A? – user200783

+0

Ничего не создается и ничего не преобразуется - оба вектора пустые – 2010-01-05 11:04:52

+0

Поведение такое же, если что-то вставляется в вектор «ints» перед построением «As». – user200783

1

Это довольно сложный вопрос, и может случиться так, что VisualStudio прав, и Комо ошибается (это кажется очень трудно поверить).

Стандарт, если читать слово за словом, определяет, что вектор конструктор в терминах конструктора копирования (см цитаты), и что в буквальном смысле означает, что объект, полученный путем разыменования итератора сначала должен быть преобразован в тип Т и то должен быть вызван конструктор копирования. На этом этапе с явным конструктором код не должен компилироваться.

С другой стороны, представляется разумным ожидать, что реализация напрямую вызовет конструктор, принимающий в качестве аргумента разыменованный итератор, и в этом случае вызов конструктора будет явным и, следовательно, код должен быть скомпилирован.Это противоречило бы точной формулировке в приведенной ниже цитате, как конструктор копии определяется для данного типа T, как конструктор, который принимает один, возможно, постоянную ссылку на объект типа T.

Я не могу думать о любой разумный аргумент не использовать подход Комоу, и я полагаю (это просто личное мнение) заключается в том, что формулировка в стандарте относительно сложности векторного конструктора, вероятно, должна быть скорректирована, так как требуется только N вызовов к соответствующему T конструктор, где это необходимо, должен быть определен как конструктор, который соответствует вызову T(*first) (то есть либо конструктор, принимающий InputIterator::value_type (по значению или, возможно, постоянную ссылку), либо копию T const ructor после того, как неявное преобразование из InputIterator::value_type к T.

23.2.4.1 [lib.vector.cons]/1

Сложность: Шаблон Конструктор вектор (InputIterator первый, InputIterator последний) составляет только N вызывает конструктор копирования T (где N - это расстояние между первым и последним) и не перераспределяет , если итераторы первого и последнего из переадресации, двунаправленного или случайного категорий доступа. Он делает порядок N вызывает конструктор копирования T и журнал заказов N перераспределений, если они просто вводят итераторы.

Я хотел бы знать, как компилятор VS ведет себя при Дано:

struct T1; 
struct T2 { 
    operator T1(); 
}; 
struct T1 { 
    T1(T2 const &) { std::cout << "T1(T2)" << std::endl; } 
}; 
T2::operator T1() { 
    std::cout << "T2::operator T1" << std::endl; 
    return T1(*this); 
} 
int main() { 
    std::vector<T2> v2; 
    v2.push_back(T2()); 
    std::vector<T1> v1(v2.begin(), v2.end()); 
} 

С г ++ результат в том, что T2::operator T1 не называется, а элементы в v1 построены непосредственно из элементов v2. Я бы предположил, что с VS компилятор будет использовать T2::operator T1 для преобразования из каждого элемента в v2 в элемент T1, а затем вызывает конструктор копирования. Это так?

+0

На самом деле попытка скомпилировать ваш код с помощью компилятора VS дает ошибку (C2664): 'std :: allocator <_Ty> :: construct': не может преобразовать параметр 2 из 'T2' в 'const T1 &' с помощью [_Ty = T1 ] Причина: невозможно преобразовать из «T2» в «const T1». Доступен оператор пользовательского преобразования, который может выполнить это преобразование, или оператор не может быть вызван – user200783

+0

Я только что протестировал с помощью бета-версии VS2010, и он не только компилируется, но и выполняет с тем же поведением, что и g ++: вызывает конструктор 'T1 (T2 const &)'. –

1

Это действительно сводится к вопросу о том, как реализована библиотека STL, а не проблема спецификации языка. В спецификации языка нет ничего, что запрещало бы это работать, и нет ничего, что требовало бы, чтобы оно работало.

Если конструктор stl :: vector был написан для попытки неявного преобразования с использованием оператора присваивания, тогда он потерпит неудачу. Скорее всего, реализация Microsoft STL использует оптимизацию возвращаемого значения во время инициализации через вызов конструктора, и в этом случае этот код будет работать нормально.

Важно отметить, что единственная причина, по которой это работает, заключается в том, что шаблон stl :: vector конструктор является шаблоном, и единственным требованием является то, что он является input_iterator или, точнее, поддерживает все необходимые функции ввода итератор.

Я также хотел бы отметить, что это яркий пример того, почему часто бывает сложно писать кросс-платформенный код. Иногда у вас возникают проблемы, когда ни один компилятор не обязательно отклоняется от языкового стандарта, но код по-прежнему не переносится.

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