Loading...
MartinBG avatar MartinBG 4803 Точки

Овърлоуд на bool оператора

В скелета на трета задача (TypedStream) намерих следния код:

	// Vector2D.h
    operator std::string() const {
		std::ostringstream s;
		s << "(" << this->x << ", " << this->y << ")";
		return s.str();
	}

Това беше ново за мен и докато го разучавах (пре)открих много други неща, свързани с овърлоудването на оператори в C++. 

Особено интересно в контекста на тази задача ми се стори овърлоудването на bool, което дава възможност за булева проверка на стейта на инстанция от класа:

TestClass test;

if (test) {
   ...
}

В конкретната задача го имплементирах така (private method в TypedStream, защото не го ползвам извън класа):

  explicit operator bool() const {
    return this->stream.good();
  }

Горното работи за задачата, но бърза справка показва, че овърлоудването на bool оператора може и да има нежелани странични ефекти (като кастване през bool до 0/1), решаването на които е свързано с доста по-сложна имплементация:

 

The Safe Bool Idiom

Restrain Conversion Operators with the "Indirect Conversion" Idiom

 

Според тази статия explicit (от C++ 11) би следвало да реши този проблем: 

Since bool is convertible to int and this conversion is not a user defined conversion, enabling an implicit conversion from a type X to bool means, any object of type X can also be implicitly converted to int, giving 0 or 1. Therefore objects of type X could participate in overload resolution in many unexpected cases which can make using X a nightmare. That has been a known problem for a long time, and looking up “safe bool idiom” will give you a lot of information of how not to covert to bool but something that is only convertible to bool. Luckily, C++11 solved the problem by introducing explicit conversion operators and stating that the compiler shall try to explicitly cast objects to bool if they are used in a boolean context, as in `if (x)`.

 

 

Въпросът ми е най-вече към Жоро:

Правилно ли съм разбрал, че explicit решава всички проблеми, произтичащи от овърлоуда на bool оператора, и ако не е така, какво друго трябва да се направи за правилната му имплементация?

Тагове:
2
C++ Programming 28/10/2018 18:53:01
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки
Best Answer

Здравей,

Хубаво си обобщил нещата, и, накратко - да, explicit в C++11 решава проблемите, които си описал, и вече е достатъчно разпространен за да се ползва почти навсякъде.

explicit общо взето казва, че за да извикаш конвертиране, трябва да го извикаш изрично. Примерно ако някакъв клас X има explicit operator std::string() { ... }, то тогава този код:

X x;

std::string s = x;

няма да се компилира, заради explicit ключовата дума. За да се компилира, ще трябва да напишем std::string s = (std::string)x. Ако не бяхме написали explicit щеше да се компилира без изричния cast (explicit е "изричен" на български).

Допълнение към това правило обаче е, че ако се ползва в bool контекст - тоест if(x), while(x), и т.н., компилаторът автоматично вмъква (bool), тоест кодът става if((bool)x), while((bool)x) и прочие. Но го вмъква само в такива контексти, не би го вмъкнал в примерно int i = x; - това ще даде компилационна грешка, защото липсва изричното (bool) което се изисква от explicit. Също няма да го вмъкне и в X x1, x2; if (x1 == x2) { ... }, докато ако няма explicit това ще се компилира и ще сравни bool стойностите на x1 и x2, което не искаме, защото bool стойностите на x1 и x2 не казват дали x1 и x2 наистина са "равни".

Така че да, explicit решава всички ситуации, поне за които се сещам. Тези неща предполагам са ти били ясни вече, но реших да ги разпиша в случай, че някой друг чете тази тема.

Това, което не решава обаче, е принципният проблем на operator overloading-а. Много хора ще кажат, че не е много добре такива conversions да се случват - в Java примерно operator overloading изобщо няма и всичко се прави с викане на методи. Викането на метод често е по-четимо и дава повече информация какво проверяваме от това да конвертираме към bool. В C++ обаче има една такава култура на "разбирачество", в която излишни неща се избягват и се очаква, че си достатъчно добър да се справиш дори с по-особен синтаксис. Примерно while(cin) е същото като while(!cin.fail()), обаче това второто почти никъде няма да го видиш, въпреки че за начинаещо е по-четимо.

Така че да, технически погледнато explicit решава проблемите, но не премахва изискването, че overload-и трябва да се правят само тогава, когато е очевидно какво означава bool проверка на един обект. За stream-овете е ясно, за smart pointer-и е ясно, обаче за класът Vehicle най-вероятно не е, и е добре да се избягва.

Поздрави,

Жоро

2
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.