Явные конструкторы C++ и итераторы

Рассмотрите следующий код:

#include 

struct A
{
    explicit A(int i_) : i(i_) {}
    int i;
};

int main()
{
    std::vector ints;
    std::vector As(ints.begin(), ints.end());
}

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

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

Однако с помощью компилятора Comeau онлайн, код компилирует успешно.

Который корректен?

Править:

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

Однако изменение vector кому: map и vector кому: map причины оба компилятора для принятия кода!

7
задан user200783 5 January 2010 в 01:07
поделиться

6 ответов

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

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

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

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

.
3
ответ дан 7 December 2019 в 10:02
поделиться

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

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

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

EDIT Однако этот компилятор компилирует:

Foo foo = (Foo)5;

Что является явным преобразованием, так что ничего страшного. Я думаю, что класс Comeau делает явное приведение где-то в конструкторе, где нет библиотеки Microsoft.

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

0
ответ дан 7 December 2019 в 10:02
поделиться

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

.
0
ответ дан 7 December 2019 в 10:02
поделиться

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

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

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

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

23.2.4.1 [lib.vector.cons]/1

Complexity: Вектор шаблона конструктора(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() );
}

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

1
ответ дан 7 December 2019 в 10:02
поделиться

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

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

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

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

1
ответ дан 7 December 2019 в 10:02
поделиться
Другие вопросы по тегам:

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