Рассмотрите следующий код:
#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
причины оба компилятора для принятия кода!
Я просмотрел реализацию STL GCC и она должна иметь похожее поведение. Вот почему
v
инициализируются общим шаблоном функции, который принимает любые два типа X
и V
и вызывает new( p ) X( v )
, где v
- это V
(я немного перефразирую). Это позволяет выполнить явное преобразование.set
или map
инициализируются закрытой функцией-членом _tree
, которая специально ожидает, что T const &
будет передана внутри. Эта функция-член не является шаблоном (кроме того, что является членом шаблона), поэтому, если исходное значение не может быть неявно преобразовано в T
, вызов не удастся. (И снова я упрощаю код.)Стандарт не требует, чтобы работало явное преобразование или чтобы неявное преобразование не работало при инициализации контейнера с диапазоном. Он просто говорит, что диапазон копируется в контейнер. Определенно неоднозначно для вашей цели.
Удивительно, что такая неоднозначность существует, учитывая, как уже дорабатывали стандарт с учетом проблем типа , которые были у меня пару недель назад.
. Я думаю, что это будет зависеть от того, как std::vector As(Iterator,Iterator)
реализован в вашей конкретной реализации STL.
Этот код не компилируется в 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
Да, он должен скомпилироваться. Если конструктор не используется, то его явность не является проблемой.
.Это довольно сложный вопрос, и, возможно, 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, а затем вызывать конструктор копирования. Неужели?
Это действительно сводится к вопросу о том, как реализована библиотека STL, а не к проблеме спецификации языка. В спецификации языка нет ничего, что запрещало бы этому работать, и нет ничего, что требовало бы, чтобы это работало.
Если бы конструктор stl :: vector был написан для попытки неявного преобразования с использованием оператора присваивания, то он завершился бы ошибкой. Более вероятно, что реализация Microsoft STL использует оптимизацию возвращаемого значения во время инициализации через вызов конструктора, и в этом случае этот код будет работать нормально.
Важно отметить, что единственная причина, по которой это работает, заключается в том, что конструктор stl :: vector является шаблоном, и единственное требование состоит в том, чтобы он был input_iterator, или, точнее, чтобы он поддерживал все необходимые функции итератора ввода .
Я также хотел бы отметить, что это яркий пример того, почему часто бывает сложно писать кросс-платформенный код. Иногда возникают проблемы, когда ни один из компиляторов не обязательно отклоняется от стандарта языка, но код по-прежнему не переносится.