Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 5.00/5: Рейтинг темы: голосов - 5, средняя оценка - 5.00
oobarbazanoo
6 / 29 / 8
Регистрация: 13.05.2015
Сообщений: 1,835
1

Почему в данном случае срабатывает неявный конструктор

15.02.2018, 01:20. Просмотров 916. Ответов 20
Метки нет (Все метки)

Почему в данном случае срабатывает неявный конструктор, хотя он и помечен explicit?

Main.cpp:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "Hugo.h"
 
Hugo f();
 
Hugo f() {
    Hugo h;
    return HugoCopy(h);
}
 
int main(void) {
 
    return 0;
}
Hugo.h:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Hugo {
public:
    Hugo() {  }  
    explicit Hugo(Hugo const&) {  }
};
 
struct HugoCopy { 
    HugoCopy(Hugo const& hugo) 
      :hugo(hugo)
    { }
    operator Hugo const&() { return hugo; }
 
private:
    Hugo const& hugo;
};
Добавлено через 23 секунды
Взял отсюда: https://stackoverflow.com/questions/...285803#4285803.

Добавлено через 8 минут
После дебага заметил что тут выходит почему-то одновременно с вызовом конструктора:
C++
1
2
3
HugoCopy(Hugo const& hugo) 
      :hugo(hugo)
    { }
Сразу после происходит вызов оператора:

C++
1
 operator Hugo const&() { return hugo; }
Кто знает, подскажите, пожалуйста, почему.
0
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
15.02.2018, 01:20
Ответы с готовыми решениями:

Как правильно вызвать конструктор вектора в данном случае?
#include <iostream> #include <fstream> #include <map> #include <vector> #include <string>...

Почему нужен   в данном случае?
Привет всем!Собственно такой вопрос:Почему в данном случае нужен   ? И у меня при написании...

Почему типы-параметры нельзя вывести в данном случае?
static class S { public static T2 Select<T, T2>(T source, Func<T, T2> f) => new T2; ...

[C]Почему bind в данном случае выдаёт ошибку Address already in use?
Пытаюсь разобраться с темой сокетов. Есть клиент и сервер,которые связываются через STREAM сокет....

20
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
6300 / 3054 / 828
Регистрация: 18.10.2014
Сообщений: 5,708
15.02.2018, 01:39 2
Цитата Сообщение от oobarbazanoo Посмотреть сообщение
Почему в данном случае срабатывает неявный конструктор, хотя он и помечен explicit?
Ничего не понятно. У вас в коде нет никаких предпосылок для использования вашего explicit конструктора. С чего вы взяли, что он у вас "срабатывает"? Где?

У вас в коде открытым тестом прописано использование конструктора HugoCopy(Hugo const& hugo) и оператора operator Hugo const&(). А они никакие не explicit.
1
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 01:51 3
TheCalligrapher, думаю, он о том, что
C++
1
2
3
4
Hugo f() {
    Hugo h;
    return HugoCopy(h);
}
При возврате из функции для построения объекта типа Hugo необходимо неявно преобразовать HugoCopy в Hugo, но конструктор копирования Hugo является explicit и поэтому неявное преобразование должно быть невозможно. Кстати, gcc и vc, код не собирают, в отличии от clang.
1
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
6300 / 3054 / 828
Регистрация: 18.10.2014
Сообщений: 5,708
15.02.2018, 02:02 4
Цитата Сообщение от Croessmah Посмотреть сообщение
необходимо преобразовать HugoCopy в Hugo, но конструктор копирования Hugo является explicit и поэтому неявное преобразование должно быть невозможно.
Хм... В данном случае преобразование HugoCopy обратно в Hugo делается через operator Hugo const&(), т.е. с преобразованием как таковым проблем нет. У меня этот код прекрасно собирается и в gcc и в clang.

Более того, даже если добавить Hugo p = f(); в main, то код собирается gcc в режиме C++17, но не собирается в режиме C++14 (именно из-за конструктора копирования), что по-видимому вызвано guaranteed copy elision в C++17.

Добавлено через 3 минуты
Хотя да, вижу что конструктор копирования вызывается в gcc...
1
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 02:03 5
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
HugoCopy обратно в Hugo делается через operator Hugo const&()
Но для построения объекта необходим конструктор копирования, который explicit.
gcc: http://rextester.com/FRWIK38971
vc: http://rextester.com/FNLWAA35042
1
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
8084 / 3983 / 547
Регистрация: 04.12.2011
Сообщений: 11,634
Записей в блоге: 9
15.02.2018, 02:09 6
Цитата Сообщение от oobarbazanoo Посмотреть сообщение
Почему в данном случае срабатывает неявный конструктор, хотя он и помечен explicit?
У меня не компилируется. Я думаю это потому что f возвращает не ссылку а копию. А конструктора допускающего преобразование нет.

То есть, константная ссылка на локальный объект потребует копирования для возврата из такой функции. Например можно и так написать. Тут явно видно что происходит. Не желает конструктор копии преобразовывать константную ссылку в аргумент требующий Hugo h. Тут конечно забавно всё выглядит так как объявлен то он именно как ссылка на константу. Но объявление ссылки аргумента функции это не объявление типа, а декларация контракта - не менять аргумент. В функции инициализируется ссылка, но ожидается внешний объект. Это поведение касается именно константной ссылки.
C++
1
2
3
4
Hugo f(){
const Hugo& h = Hugo();
return h;
}

Не по теме:

Пардон. До моего поста выступлений ещё не было. Впрочем, я уверен в том, что говорю не менее чем обычно. То есть сомневаюсь как всегда. :pardon:

0
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 02:13 7
the return statement initializes the glvalue result or prvalue result object of the (explicit or implicit) function call by copy-initialization (11.6) from the operand.
C++
1
2
3
4
5
int main()
{
    Hugo h(HugoCopy(h));//direct-initialization - ok
    Hugo h = HugoCopy(h);//copy-initialization - ошибка
}
1
tmpValue
41 / 74 / 15
Регистрация: 04.10.2017
Сообщений: 284
15.02.2018, 02:24 8
oobarbazanoo, потому что hugo надо заменить на hoggy.
4
IGPIGP
15.02.2018, 02:27
  #9

Не по теме:

Цитата Сообщение от tmpValue Посмотреть сообщение
потому что hugo надо заменить
Причём вызывать явно и никак иначе. Про преобразования я зря понаписал. :pardon:

0
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 02:27 10
TheCalligrapher, в return statement должна быть copy-initialization.
A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor .
...
A non-explicit copy/move constructor (15.8) is a converting constructor.
For copy-initialization, the candidate functions are all the converting constructors (15.3.1) of that class.
то есть при copy-initialization explicit Hugo(Hugo const&) не должен быть кандидатом, но если return HugoCopy(h) собирается, значит в return statement используется не copy-initialization, верно?
1
oobarbazanoo
6 / 29 / 8
Регистрация: 13.05.2015
Сообщений: 1,835
15.02.2018, 14:46  [ТС] 11
tmpValue, почему на hoggy? Не нахожу перевода или значения данного слова. Объясните данный момент, пожалуйста.

Добавлено через 1 минуту
Croessmah, в этом то и вопрос. Как?

Добавлено через 4 минуты
Croessmah, подскажите, пожалуйста, как Вы смогли сделать так, что бы Ваш код сохранился для тех кто перейдёт по ссылке на онлайн компилятор?
0
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 17:10 12
Цитата Сообщение от oobarbazanoo Посмотреть сообщение
почему на hoggy? Не нахожу перевода или значения данного слова.
Может у него и спросим? hoggy, выходи, дорогой.
Цитата Сообщение от oobarbazanoo Посмотреть сообщение
как Вы смогли сделать так, что бы Ваш код сохранился для тех кто перейдёт по ссылке на онлайн компилятор?
Как правило, облачные компиляторы умеют это. Где-то кнопочка save, где-то fork, где-то share...

Добавлено через 1 минуту
Цитата Сообщение от oobarbazanoo Посмотреть сообщение
Croessmah, в этом то и вопрос. Как?
Ну так мне тоже интересно. Возможно, TheCalligrapher, сможет объяснить.
Или DrOffset, или ct0r.
На удачу позову еще FoReVeR и Tulosba.
1
hoggy
Эксперт С++
7806 / 3550 / 744
Регистрация: 15.11.2014
Сообщений: 8,095
Завершенные тесты: 1
15.02.2018, 19:07 13
Цитата Сообщение от Croessmah Посмотреть сообщение
Может у него и спросим? hoggy, выходи, дорогой.
привеееет))


по теме: не должно компиляццо.
1
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
8084 / 3983 / 547
Регистрация: 04.12.2011
Сообщений: 11,634
Записей в блоге: 9
15.02.2018, 19:16 14
Цитата Сообщение от hoggy Посмотреть сообщение
по теме: не должно компиляццо.
Дык и не компилится. Однако не ясно почему вот так:
C++
1
2
A b;
A a = b;//неявный вызов
в то время как в функциональном стиле - explisit работает нормально.
Оно же и при возврате из функции по значению получается неявным вызовом... Ну то есть, такое чувство, что гарантия что не появится приведение только при вызове:
C++
1
A a(b);
иначе параметр будет обнюхиваться и может быть приведен. В С++ приведение к себе - не преступление, конечно, но похоже что exlisit счтает и это вольностью.
Понимаю что бредово, но вот как ещё это можно объяснить?
1
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 19:31 15
Цитата Сообщение от IGPIGP Посмотреть сообщение
в то время как в функциональном стиле - explisit работает нормально.
Потому что по правилам языка в direct-initialization A a(b) explicit конструкторы будут рассматриваться как кандидаты при выборе конструктора, а в copy-initialization A a = b; explicit-конструкторы не являются кандидатами.

Добавлено через 44 секунды
Цитата Сообщение от IGPIGP Посмотреть сообщение
Дык и не компилится.
clang собирает
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
8084 / 3983 / 547
Регистрация: 04.12.2011
Сообщений: 11,634
Записей в блоге: 9
15.02.2018, 19:42 16
Цитата Сообщение от Croessmah Посмотреть сообщение
Потому что по правилам языка
Вот-вот. Словосочетание "правилам языка" - ключевое. Это в общем и целом означает "потому что". Фактически операторная форма вызова:
A a= b;
однозначно эквивалентна
A a(a);
и не должно быть разницы. Различие то тут (насколько я понимаю) только в синтаксисе, а вызов то однозначен? Просто компиллятору нужно бы быть чуть-чуть умнее.
Цитата Сообщение от Croessmah Посмотреть сообщение
clang собирает
и выясняется, что такой компилятор есть.
Странно это. Но нужно запомнить.
0
Croessmah
++Ͻ
16221 / 9311 / 1784
Регистрация: 27.09.2012
Сообщений: 22,985
Записей в блоге: 2
Завершенные тесты: 2
15.02.2018, 19:45 17
Цитата Сообщение от IGPIGP Посмотреть сообщение
однозначно эквивалентна
Не эквивалентна. Это два разных вида инициализации. Это четко прописано в стандарте языка.
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
8084 / 3983 / 547
Регистрация: 04.12.2011
Сообщений: 11,634
Записей в блоге: 9
15.02.2018, 19:50 18
Цитата Сообщение от Croessmah Посмотреть сообщение
Не эквивалентна.
Я имел ввиду случай когда есть определённый пользователем конструктор копии (тот что в топике случай). Конструктор преобразования с аргументом своего типа, то есть. Насколько мне известно при вызове:
A a=b;
должен вызваться именно он и ни какой другой. Если это не так, то это и есть ответ на вопрос топика.
0
rat0r
284 / 175 / 21
Регистрация: 16.02.2018
Сообщений: 666
29.04.2018, 03:24 19
Цитата Сообщение от Croessmah Посмотреть сообщение
При возврате из функции для построения объекта типа Hugo необходимо неявно преобразовать HugoCopy в Hugo, но конструктор копирования Hugo является explicit и поэтому неявное преобразование должно быть невозможно.
Не всё так однозначно. Конечно, return e — это copy-initialization.
Только вот в зависимости от типа (и value category) e правила разные.
В данном случае, возвращаемый тип у функции — это тип-класс.
Поэтому смотрим в
Цитата Сообщение от http://eel.is/c++draft/dcl.init#17.6
If the destination type is a (possibly cv-qualified) class type:
(17.6.1) — If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.
[ Example: T x = T(T(T())); calls the T default constructor to initialize x. — end example]
(17.6.2) — Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered.
The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution.
The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s).
If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
(17.6.3) — Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution.
If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
The function selected is called with the initializer expression as its argument; if the function is a constructor, the call is a prvalue of the cv-unqualified version of the destination type whose result object is initialized by the constructor.
The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.
Если тип у e — тот же (игнорируя cv-квалификаторы) тип-класс, что и инициализируемый (возвращаемый), и e это не prvalue (пункт (17.6.1)), то применяется (17.6.2) и, конечно, explicit-конструктор не подойдёт.

Но в случае return HugoCopy(h) действует пункт (17.6.3)
Пункт ссылается на [over.match.copy]
Цитата Сообщение от http://eel.is/c++draft/over.match.copy
1 Under the conditions specified in [dcl.init], as part of a copy-initialization of an object of class type, a user-defined conversion can be invoked to convert an initializer expression to the type of the object being initialized.
Overload resolution is used to select the user-defined conversion to be invoked.
[ Note: The conversion performed for indirect binding to a reference to a possibly cv-qualified class type is determined in terms of a corresponding non-reference copy-initialization. — end note]
Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:

(1.1) The converting constructors of T are candidate functions.
(1.2) When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of S and its base classes are considered.
When initializing a temporary object ([class.mem]) to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion functions are also considered.
Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions.
Conversion functions that return “reference to X” return lvalues or xvalues, depending on the type of reference, of type X and are therefore considered to yield X for this process of selecting candidate functions.

2 In both cases, the argument list has one argument, which is the initializer expression.
[ Note: This argument will be compared against the first parameter of the constructors and against the implicit object parameter of the conversion functions. — end note]
(1.1) не даёт ничего, т.к. никаких converting functions у Hugo нет.
(1.2) даёт нам candidate function HugoCopy::operator Hugo const&.
Эта функция, с аргументом HugoCopy(h), используется для инициализации: "The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization".

Это свежий драфт. В драфте n1905 от 2005-го года сказано, в принципе, то же самое:
The result of the call (which is the temporary for the constructor case) is then used to direct-initialize,
according to the rules above, the object that is the destination of the copy-initialization
Цитата Сообщение от hoggy Посмотреть сообщение
по теме: не должно компиляццо.
Точнее, должно.

Добавлено через 22 часа 59 минут
Самое забавное, что Croessmah 2.5 года назад цитировал нужное место стандарта и даже подчеркнул:
Цитата Сообщение от Croessmah Посмотреть сообщение
The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized
но в этом топике старательно всех убеждает, что код компилироваться не должен.
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
8084 / 3983 / 547
Регистрация: 04.12.2011
Сообщений: 11,634
Записей в блоге: 9
29.04.2018, 08:42 20
Последнее, скорее всего относится к копи-элизиум оптимизации и к данному случаю не относится. Я не уверен, потому и пишу "скорее всего". В нашем случае (опять же - по идее) имеет место быть возможность прямого указания компилятору - применять копирующий конструктор только для случаев его явного вызова. Это может быть нужно, например, когда операция присаивания и копирующий конструктор работают с ресурсами по разному.
0
29.04.2018, 08:42
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
29.04.2018, 08:42

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Почему в данном коде не срабатывает присвоение
Всем привет, подскажите пожалуйста почему в данном коде не срабатывает присвоение ...

Почему в данном случае ставится двойной нижний пробел __ в цикле for?
>>> def fib(n): a = 0 b = 1 for __ in range(n): a = b + b return a >>>

Почему в данном случае работа с заранее выделенной памятью медленнее чем с динамической?
Написал функцию которая на основе списка выделяет память и при каждом вызове возвращает указатель...

Почему не срабатывает конструктор копирования в пользовательском классе
вроде со всем разобралась, но не заходит в конструктор копирования. В чём ошибка? #include...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2020, vBulletin Solutions, Inc.