Важное отличие, которое следует отметить, в дополнение к другим ответам, заключается в том, что yield
в C # нельзя использовать как выражение, только как утверждение.
Пример использования выражения yield
в Python (пример вставлен из здесь ):
def echo(value=None):
print "Execution starts when 'next()' is called for the first time."
try:
while True:
try:
value = (yield value)
except GeneratorExit:
# never catch GeneratorExit
raise
except Exception, e:
value = e
finally:
print "Don't forget to clean up when 'close()' is called."
generator = echo(1)
print generator.next()
# Execution starts when 'next()' is called for the first time.
# prints 1
print generator.next()
# prints None
print generator.send(2)
# prints 2
generator.throw(TypeError, "spam")
# throws TypeError('spam',)
generator.close()
# prints "Don't forget to clean up when 'close()' is called."
Предполагая, что вы хотите, чтобы специализированная версия была выбрана для любого целочисленного типа (а не только int в частности, вы могли бы сделать это как функцию шаблона и использовать Boost. EnableIf для удаления этих перегрузок из доступного набора перегрузок, если операнд не является целочисленным типом.
#include <cstdio>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
class CFixed
{
public:
CFixed( int ) {}
CFixed( float ) {}
};
CFixed operator* ( const CFixed& a, const CFixed& )
{ puts("General CFixed * CFixed"); return a; }
template <class T>
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* ( const CFixed& a, T )
{ puts("CFixed * [integer type]"); return a; }
template <class T>
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* ( T , const CFixed& b )
{ puts("[integer type] * CFixed"); return b; }
int main()
{
CFixed(0) * 10.0f;
5 * CFixed(20.4f);
3.2f * CFixed(10);
CFixed(1) * 100u;
}
Естественно, вы также можете использовать другое условие, чтобы сделать эти перегрузки доступными, только если T = int: typename boost :: enable_if
Что касается проектирования класса, возможно, вы могли бы больше полагаться на шаблоны. Например, конструктор может быть шаблоном и, опять же,если вам нужно различать целые и действительные типы, должна быть возможность использовать этот метод.
Если у вас есть конструкторы, которые можно вызывать только с одним аргументом, вы фактически создали оператор неявного преобразования. В вашем примере везде, где требуется CFixed
, можно передать как int
, так и float
. Это, конечно, опасно, потому что компилятор может молча сгенерировать код, вызывающий неправильную функцию, вместо того, чтобы лаять на вас, когда вы забыли включить объявление какой-либо функции.
Поэтому хорошее практическое правило гласит, что всякий раз, когда вы пишете конструкторы, которые могут быть вызваны только с одним аргументом (обратите внимание, что этот foo (int i, bool b = false)
может быть вызван с одним аргументом, даже если он принимает два аргумента), вы должны сделать этот конструктор явным
, если вы действительно не хотите, чтобы сработало неявное преобразование. явные
конструкторы не используются компилятором для неявных преобразований.
Вам придется изменить свой класс на этот:
class CFixed
{
explicit CFixed( int );
explicit CFixed( float );
};
Я обнаружил, что есть очень мало исключений из этого правила. ( std :: string :: string (const char *)
- довольно известный пример.)
Изменить: Извините, я упустил момент о запрете неявных преобразований из int
до float
.
Я вижу единственный способ предотвратить это - также предоставить операторы для float
.
int
в float
.
Я вижу единственный способ предотвратить это - также предоставить операторы для float
.
int
в float
.
Я вижу единственный способ предотвратить это - также предоставить операторы для float
.
Вы также должны перегрузить типом float
. Преобразование из int
в указанный пользователем тип ( CFixed
) имеет более низкий приоритет, чем встроенное преобразование с плавающей запятой в float
. Таким образом, компилятор всегда будет выбирать функцию с int
, если вы не добавите функцию с float
.
Подробнее см. В разделе 13.3 стандарта C ++ 03. Почувствуй боль.
Кажется, я тоже потерял его из виду. :-( UncleBens сообщает, что добавление только числа с плавающей запятой не решает проблему, так как версия с double
также должна быть добавлена. Но в любом случае добавление нескольких операторов, связанных со встроенными типов утомительно, но не приводит к комбинаторному повышению.
Как насчет того, чтобы сделать преобразование явным ?
Согласитесь с sbi, вы обязательно должны сделать свои однопараметрические конструкторы явными.
Однако вы можете избежать взрывного роста в функциях operator <>, которые вы пишете с помощью шаблонов:
template <class T>
CFixed operator* ( const CFixed& a, T b )
{ ... }
template <class T>
CFixed operator* ( T a, const CFixed& b )
{ ... }
В зависимости от от того, какой код находится в функциях, он будет компилироваться только с теми типами, из которых вы поддерживаете преобразование.