Есть код (прошу не спрашивать почему такой, это выжимка из большой библиотеки ):
struct A
{
operator size_t() const;
operator std::vector<std::string>() const;
};
// Где-то ниже....
A a;
std::vector<string_t> v1 = a; // (1) ОК
std::vector<string_t> v2(a); // (2) Error - Ambiguous...
Можно пояснить как логика тут работает (а-то я умудрился "пройти мимо")
В случае (1) — не можем найти конструктор из A() и вызывает оператор преобразования типа std::vector<std::string>. (Как точно называется это синтаксис?)
В случае (2) — пытаемся вызвать конструктор сразу и не можем выбрать преобразования, т.к. оба подходят.
У них действительно разная логика порядка поиска преобразований или я что-то путаю?
Какая возникает проблема:
struct B
{
B(const A& a) : v(a) {} // Тут используется логика и синтаксис варианта (2). Соответственно - Error - Ambiguous...
std::vector<std::string> v;
};
Как-то можно в списке инициализации сказать что нужно делать как в случае (1) ?
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>Скастить к нужному типу.
Пример можно ? Если static_cast — то не кастится, т.к. ambiguous...
Ну и вообще, от какста с указанием типа собственно и хотелось уйти, т.к. тип описывается очень длинно.
Здравствуйте, Videoman, Вы писали:
V>Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>>Скастить к нужному типу.
V>Пример можно ? Если static_cast — то не кастится, т.к. ambiguous...
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>... SVZ>Проверил на VS2017 и gcc-8.2.0
SVZ>Вообще не люблю каст операторы. Очень много сюрпризов несут в себе. SVZ>В 99% случаев можно сделать нормальные мембер функции, возвращающие нужные данные.
Спасибо! Ну вот такого вот каста и хотелось бы избежать. В реальности у меня там очень навороченные и длинные шаблоны. А по поводу первого вопроса, можете пояснить? Может быть можно как-то исхитриться и не указывать весть тип в рамках С++17 ?
собственно проблема в том, что есть различные конструкторы vector. Один принимает std::vector<std::string> (конструктор копирования), другой принимает параметр size_t — количество элементов в векторе. a одинаково хорошо приводится как к первому так и ко второму. Поэтому vector и кричит об Ambiguous.
Чтобы убрать неоднозначность можно вызвать явно оператор, который вы хотите.
std::vector<string_t> v2(a.operator std::vector<string>())
или
std::vector<string_t> v2(a.operator size_t())
p.s. Ну да. Почему собственно код (1) компилируется. Там объект vector уже существует и никаких конструкторов не вызывается. Вызывается оператор присваивания. Этот оператор присваивания size_t принимать никак не может. Поэтому тут неоднозначности не возникает
Здравствуйте, sergii.p, Вы писали:
SP>собственно проблема в том, что есть различные конструкторы vector. Один принимает std::vector<std::string> (конструктор копирования), другой принимает параметр size_t — количество элементов в векторе. a одинаково хорошо приводится как к первому так и ко второму. Поэтому vector и кричит об Ambiguous.
SP>Чтобы убрать неоднозначность можно вызвать явно оператор, который вы хотите. SP>std::vector<string_t> v2(a.operator std::vector<string>()) SP>или SP>std::vector<string_t> v2(a.operator size_t())
Это я понял. Собственно именно это в вопросе и описано. Даже касты подобрал что бы был видна неоднозначность.
SP>p.s. Ну да. Почему собственно код (1) компилируется. Там объект vector уже существует и никаких конструкторов не вызывается. Вызывается оператор присваивания. Этот оператор присваивания size_t принимать никак не может. Поэтому тут неоднозначности не возникает.
А как-нибудь можно добиться синтаксиса — v2(a) — что бы работало как мне нужно?
Здравствуйте, sergii.p, Вы писали:
SP>ну компилятор то уже сказал ответ. Синтаксис v2(a) невозможен.
В лоб — да, не может. Просто я надеялся что можно как-то, чуть изменив синтаксис, сказать что я хочу использовать вариант (1) — ну мало ли
Т.е. максимум что я могу сделать это — v2(a.As<decltype(v2)>()) — что-то такое ?
Просто странно что нет эквивалента std::vector<std::string> v2 = a, но для списка инициализации.
Здравствуйте, Videoman, Вы писали:
V>Можно пояснить как логика тут работает (а-то я умудрился "пройти мимо") V>В случае (1) — не можем найти конструктор из A() и вызывает оператор преобразования типа std::vector<std::string>. (Как точно называется это синтаксис?) V>В случае (2) — пытаемся вызвать конструктор сразу и не можем выбрать преобразования, т.к. оба подходят. V>У них действительно разная логика порядка поиска преобразований или я что-то путаю?
Судя по тому, что свежие версии g++ (9.1.0) и clang++ (8.0.0)единодушно компилируют этот код с флагами -std=c++17 -Wall -Wextra -Werror -pedantic-errors, наблюдаемое поведение практически наверняка является багом в Microsoft Visual C++.
Здравствуйте, sergii.p, Вы писали:
SP>Почему собственно код (1) компилируется. Там объект vector уже существует и никаких конструкторов не вызывается. Вызывается оператор присваивания. Этот оператор присваивания size_t принимать никак не может. Поэтому тут неоднозначности не возникает
Запись вида X x = y; не предполагает вызов оператора присваивания, а подразумевает выполнение copy initialization.
Здравствуйте, Constructor, Вы писали:
C>Судя по тому, что свежие версии g++ (9.1.0) и clang++ (8.0.0)единодушно компилируют этот код с флагами -std=c++17 -Wall -Wextra -Werror -pedantic-errors, наблюдаемое поведение практически наверняка является багом в Microsoft Visual C++.
А вы не могли бы пояснить в чем бага и какой порядок подстановок должен быть по стандарту?
Здравствуйте, Constructor, Вы писали:
C>Запись вида X x = y; не предполагает вызов оператора присваивания, а подразумевает выполнение copy initialization.
Я в курсе, просто забыл поправить коллегу. Да, но при copy_initialization совершенно другой порядок подстановок. Так и должно быть?
P.S. Уже глянул приведенную вами ссылку. Да, все верно, так и должно быть
V>Можно пояснить как логика тут работает (а-то я умудрился "пройти мимо") V>В случае (1) — не можем найти конструктор из A() и вызывает оператор преобразования типа std::vector<std::string>. (Как точно называется это синтаксис?) V>В случае (2) — пытаемся вызвать конструктор сразу и не можем выбрать преобразования, т.к. оба подходят. V>У них действительно разная логика порядка поиска преобразований или я что-то путаю?
Разница здесь в том, что (1) хочет вызвать конструктор копирования и пытается привести a к нужному типу (операторами преобразования или не-explicit конструкторами). А 2 смотрит все подходящие конструкторы, и среди
них выбирает наиболее подходящий.
Кстати, в C++17 тут не будет ошибки, потому как компилятор в обоих случаях предпочтет вызвать std::vector<std::string>(std::vector<std::string>&&).
Здравствуйте, Constructor, Вы писали:
C>Судя по тому, что свежие версии g++ (9.1.0) и clang++ (8.0.0)единодушно компилируют этот код с флагами -std=c++17 -Wall -Wextra -Werror -pedantic-errors, наблюдаемое поведение практически наверняка является багом в Microsoft Visual C++.
Это из-за copy elision, который появился в C++17. Компиляция в режиме -std=c++11 все так же должна приводить к ошибке.
Здравствуйте, andrey.desman, Вы писали:
AD>Разница здесь в том, что (1) хочет вызвать конструктор копирования и пытается привести a к нужному типу (операторами преобразования или не-explicit конструкторами). А 2 смотрит все подходящие конструкторы, и среди AD> них выбирает наиболее подходящий.
Не совсем так. В (1) случае тут copy_initialization и там там другие цепочка подбора. Здесь у меня как раз все ок.
Во втором случае direct_initialization и там правила как при вызове перегруженных функций.
AD>Кстати, в C++17 тут не будет ошибки, потому как компилятор в обоих случаях предпочтет вызвать std::vector<std::string>(std::vector<std::string>&&).
Можете ткнуть где это описано? В том то и парадокс, что у меня не работает по VS2017, который якобы С++17.
Здравствуйте, Videoman, Вы писали:
AD>>Разница здесь в том, что (1) хочет вызвать конструктор копирования и пытается привести a к нужному типу (операторами преобразования или не-explicit конструкторами). А 2 смотрит все подходящие конструкторы, и среди AD>> них выбирает наиболее подходящий. V>Не совсем так. В (1) случае тут copy_initialization и там там другие цепочка подбора. Здесь у меня как раз все ок. V>Во втором случае direct_initialization и там правила как при вызове перегруженных функций.
А я что написал?
AD>>Кстати, в C++17 тут не будет ошибки, потому как компилятор в обоих случаях предпочтет вызвать std::vector<std::string>(std::vector<std::string>&&). V>Можете ткнуть где это описано? В том то и парадокс, что у меня не работает по VS2017, который якобы С++17.
Copy elision.
First, if T is a class type and the initializer is a prvalue expression whose cv-unqualified type is the same class as T, the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object: see copy elision
Про std::vector<std::string>(std::vector<std::string>&&) я наврал. copy elision как раз про прямую инициализацию, а не перемещение. Но тем не менее.
Мне не понятно про prvalue, если там вообще-то lvalue. Может, это наоборот gcc слишком усердствует...