Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 11:02
Оценка:
Есть код (прошу не спрашивать почему такой, это выжимка из большой библиотеки ):
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) ?
Отредактировано 07.08.2019 11:04 Videoman . Предыдущая версия .
//
Re: Вопрос про конструкторы
От: Stanislav V. Zudin Россия  
Дата: 07.08.19 11:47
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Как-то можно в списке инициализации сказать что нужно делать как в случае (1) ?


Скастить к нужному типу.
_____________________
С уважением,
Stanislav V. Zudin
Re[2]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 12:26
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Скастить к нужному типу.


Пример можно ? Если static_cast — то не кастится, т.к. ambiguous...
Ну и вообще, от какста с указанием типа собственно и хотелось уйти, т.к. тип описывается очень длинно.
Отредактировано 07.08.2019 12:28 Videoman . Предыдущая версия .
Re[3]: Вопрос про конструкторы
От: Stanislav V. Zudin Россия  
Дата: 07.08.19 12:42
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Здравствуйте, Stanislav V. Zudin, Вы писали:


SVZ>>Скастить к нужному типу.


V>Пример можно ? Если static_cast — то не кастится, т.к. ambiguous...


#include <iostream>
#include <vector>
#include <string>

struct A
{
    operator size_t() const
    {
        return m_data.size();
    }

    operator std::vector<std::string>() const
    {
        return m_data;
    }

private:
    std::vector<std::string> m_data;
};

struct B
{
    B(const A& a) : v(static_cast<const std::vector<std::string>&>(a)) {}

    std::vector<std::string> v;
};


int main()
{
    std::cout << "Hello World!\n"; 
    A a;
    std::vector<std::string> v1 = a;
    std::vector<std::string> v2( static_cast<const std::vector<std::string>&>(a));

    B b(a);
    return 0;
}


Проверил на VS2017 и gcc-8.2.0

V>Ну и вообще, от какста с указанием типа собственно и хотелось уйти, т.к. тип описывается очень длинно.


Вообще не люблю каст операторы. Очень много сюрпризов несут в себе.
В 99% случаев можно сделать нормальные мембер функции, возвращающие нужные данные.
_____________________
С уважением,
Stanislav V. Zudin
Re[4]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 12:50
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>...

SVZ>Проверил на VS2017 и gcc-8.2.0

SVZ>Вообще не люблю каст операторы. Очень много сюрпризов несут в себе.

SVZ>В 99% случаев можно сделать нормальные мембер функции, возвращающие нужные данные.

Спасибо! Ну вот такого вот каста и хотелось бы избежать. В реальности у меня там очень навороченные и длинные шаблоны. А по поводу первого вопроса, можете пояснить? Может быть можно как-то исхитриться и не указывать весть тип в рамках С++17 ?
Re: Вопрос про конструкторы
От: sergii.p  
Дата: 07.08.19 12:55
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Есть код (прошу не спрашивать почему такой, это выжимка из большой библиотеки ):

V>
V>struct A
V>    {
V>        operator size_t() const;
V>        operator std::vector<std::string>() const;
V>    };

V>// Где-то ниже....

V>A a;
V>std::vector<string_t> v1 = a; // (1) ОК
V>std::vector<string_t> v2(a);  // (2) Error - Ambiguous...
V>


собственно проблема в том, что есть различные конструкторы 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 принимать никак не может. Поэтому тут неоднозначности не возникает
Отредактировано 07.08.2019 12:59 sergii.p . Предыдущая версия .
Re[2]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 13:12
Оценка:
Здравствуйте, 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) — что бы работало как мне нужно?
Re[3]: Вопрос про конструкторы
От: sergii.p  
Дата: 07.08.19 13:35
Оценка:
Здравствуйте, Videoman, Вы писали:

V>А как-нибудь можно добиться синтаксиса — v2(a) — что бы работало как мне нужно?


ну компилятор то уже сказал ответ. Синтаксис v2(a) невозможен.
Re[4]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 13:44
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>ну компилятор то уже сказал ответ. Синтаксис v2(a) невозможен.


В лоб — да, не может. Просто я надеялся что можно как-то, чуть изменив синтаксис, сказать что я хочу использовать вариант (1) — ну мало ли

Т.е. максимум что я могу сделать это — v2(a.As<decltype(v2)>()) — что-то такое ?
Просто странно что нет эквивалента std::vector<std::string> v2 = a, но для списка инициализации.
Отредактировано 07.08.2019 13:45 Videoman . Предыдущая версия .
Re: Вопрос про конструкторы
От: Constructor  
Дата: 07.08.19 13:49
Оценка:
Здравствуйте, 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++.
Re[2]: Вопрос про конструкторы
От: Constructor  
Дата: 07.08.19 13:54
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Почему собственно код (1) компилируется. Там объект vector уже существует и никаких конструкторов не вызывается. Вызывается оператор присваивания. Этот оператор присваивания size_t принимать никак не может. Поэтому тут неоднозначности не возникает


Запись вида X x = y; не предполагает вызов оператора присваивания, а подразумевает выполнение copy initialization.
Re[2]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 13:58
Оценка:
Здравствуйте, Constructor, Вы писали:

C>Судя по тому, что свежие версии g++ (9.1.0) и clang++ (8.0.0) единодушно компилируют этот код с флагами -std=c++17 -Wall -Wextra -Werror -pedantic-errors, наблюдаемое поведение практически наверняка является багом в Microsoft Visual C++.


А вы не могли бы пояснить в чем бага и какой порядок подстановок должен быть по стандарту?
Re[3]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 14:01
Оценка:
Здравствуйте, Constructor, Вы писали:

C>Запись вида X x = y; не предполагает вызов оператора присваивания, а подразумевает выполнение copy initialization.


Я в курсе, просто забыл поправить коллегу. Да, но при copy_initialization совершенно другой порядок подстановок. Так и должно быть?
P.S. Уже глянул приведенную вами ссылку. Да, все верно, так и должно быть
Отредактировано 07.08.2019 14:09 Videoman . Предыдущая версия .
Re: Вопрос про конструкторы
От: andrey.desman  
Дата: 07.08.19 14:31
Оценка:
Здравствуйте, Videoman, Вы писали:

V>
V>struct A
V>    {
V>        operator size_t() const;
V>        operator std::vector<std::string>() const;
V>    };

V>// Где-то ниже....

V>A a;
V>std::vector<string_t> v1 = a; // (1) ОК
V>std::vector<string_t> v2(a);  // (2) Error - Ambiguous...
V>

V>Можно пояснить как логика тут работает (а-то я умудрился "пройти мимо")
V>В случае (1) — не можем найти конструктор из A() и вызывает оператор преобразования типа std::vector<std::string>. (Как точно называется это синтаксис?)
V>В случае (2) — пытаемся вызвать конструктор сразу и не можем выбрать преобразования, т.к. оба подходят.
V>У них действительно разная логика порядка поиска преобразований или я что-то путаю?

1 — это copy initialization, а 2 — это direct initialization.

Разница здесь в том, что (1) хочет вызвать конструктор копирования и пытается привести a к нужному типу (операторами преобразования или не-explicit конструкторами). А 2 смотрит все подходящие конструкторы, и среди
них выбирает наиболее подходящий.
Кстати, в C++17 тут не будет ошибки, потому как компилятор в обоих случаях предпочтет вызвать std::vector<std::string>(std::vector<std::string>&&).
Re[2]: Вопрос про конструкторы
От: andrey.desman  
Дата: 07.08.19 14:43
Оценка:
Здравствуйте, 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 все так же должна приводить к ошибке.
Re[2]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 14:45
Оценка:
Здравствуйте, 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.
Re[3]: Вопрос про конструкторы
От: andrey.desman  
Дата: 07.08.19 15:16
Оценка:
Здравствуйте, 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 слишком усердствует...
Re: Вопрос про конструкторы
От: night beast СССР  
Дата: 07.08.19 16:20
Оценка: +1
Здравствуйте, Videoman, Вы писали:

V>Есть код (прошу не спрашивать почему такой, это выжимка из большой библиотеки ):

V>
V>struct A
V>    {
V>        operator size_t() const;
V>        operator std::vector<std::string>() const;
V>    };
V>

V>Как-то можно в списке инициализации сказать что нужно делать как в случае (1) ?

std::vector<std::string> to_vector(const A&);

std::vector<std::string> v2(to_vector(a));
Отредактировано 07.08.2019 17:17 night beast . Предыдущая версия .
Re[2]: Вопрос про конструкторы
От: Videoman Россия https://hts.tv/
Дата: 07.08.19 17:51
Оценка:
Здравствуйте, night beast, Вы писали:

NB>
NB>std::vector<std::string> to_vector(const A&);

NB>std::vector<std::string> v2(to_vector(a));
NB>

Спасибо, но в моей ситуации точно не подойдет, т.к. в реалии у меня:
A — имеет шаблонный оператор приведения типа:
template <typename Type, std::enable_if<is_compatible_v<Type>>>
operator Type() const;

, а тип v2 может описывать капец каким длинным шаблоном — типа:
std::vector<std::vector<std::map<..... // тут легко может быть все что угодно любой степени влоденности

Самое обидное, что:
(1) [Type...] a = v2;    // (Copy initialization) Ok
(2) [Type...] a(v2);     // (Direct initialization) Error
(3) [Type...] a; a = v2; // (Assign) Ok
Re[3]: Вопрос про конструкторы
От: night beast СССР  
Дата: 07.08.19 18:06
Оценка:
Здравствуйте, Videoman, Вы писали:

NB>>
NB>>std::vector<std::string> to_vector(const A&);

NB>>std::vector<std::string> v2(to_vector(a));
NB>>

V>Спасибо, но в моей ситуации точно не подойдет, т.к. в реалии у меня:

неявные операторы приведения могут принести некоторые неожиданные проблемы.
вспомни пример каста в булу в optional.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.