Явные конструкторы и итераторы 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'
что правильно?
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
Да, он должен компилироваться. Если конструктор не используется, то его эксплицитность не является проблемой.