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

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

#include <vector>

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

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

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

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

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

что правильно?

Edit:

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

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

6 ответов


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

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

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

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


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


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

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

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

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

23.2.4.1 [lib.вектор.минусы]/1

сложность: вектор шаблона конструктора (InputIterator во-первых, InputIterator последний) делает только N вызовы конструктора копирования T (где N-расстояние между первый и последний) и никаких перераспределений если итераторы first и last вперед, двунаправленный, или случайный категории доступа. Он делает заказ 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::operator T1 не называется, а скорее элементы в v1 создаются непосредственно из элементов v2. Я бы предположил, что с VS компилятор будет использовать T2::operator T1 конвертировать из каждого элемента в v2 к элементу T1, а затем вызовите конструктор копирования. Так ли это?


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

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

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

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


этот код не компилируется в 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".

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

редактировать это компилирует:

Foo foo = (Foo)5;

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

подробнее о явных конструкторах -http://www.glenmccl.com/tip_023.htm


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