Вот, достало ловить ошибки с неинициализованными членами больших классов, вида
class MyBigClass
{
int m_mySmallValue;
...
MyBigClass();
MyBigClass(X x, Y y, Z z);
... и еще куча конструкторов
};
MyBigClass::MyBigClass(xxxx) : ......
// а про малюсенькое данное забыли :(
{
}
Поэтому сделал шаблон самоинициализирующихся значений простых типов (числа, указатели).
template< class T, T i = 0 >
class auto_value
{
T t_;
public:
typedef T data_t;
typedef auto_value& self_t;
// конструктор по умолчанию - главное достоинство этой тулзыinline auto_value() : t_(i) {}
// конструктор с 1 параметром (в том числе - конструктор копирования)template< class V >
inline auto_value(V v) : t_(v) {}
// доступ к данномуinline const T& data() const { return t_; }
inline T& data() { return t_; }
// считается, что исходный тип - простойinline operator T () const { return t_; }
inline operator T& () { return t_; }
// операторы присваиванияtemplate< class V > inline self_t operator = (V v) { t_ = v; return *this; }
template< class V > inline self_t operator += (V v) { t_ += v; return *this; }
template< class V > inline self_t operator -= (V v) { t_ -= v; return *this; }
template< class V > inline self_t operator *= (V v) { t_ *= v; return *this; }
template< class V > inline self_t operator /= (V v) { t_ /= v; return *this; }
template< class V > inline self_t operator %= (V v) { t_ %= v; return *this; }
template< class V > inline self_t operator &= (V v) { t_ &= v; return *this; }
template< class V > inline self_t operator |= (V v) { t_ |= v; return *this; }
template< class V > inline self_t operator ^= (V v) { t_ ^= v; return *this; }
template< class V > inline self_t operator <<= (V v) { t_ <<= v; return *this; }
template< class V > inline self_t operator >>= (V v) { t_ >>= v; return *this; }
};
Такая куча операторов ??= нужна, чтобы не писать постоянно "myvalue.data() ??= 123"
Пример использования:
class MyBigClass
{
auto_value<bool/* , false */ > m_bEnabled;
...
public:
MyBigClass() {} // все члены инициализируются сами - как сумеют
...
bool isEnabled() { return m_bEnabled; } // получим false, а не 0xCDCDCDCDvoid setEnabled(bool b) { m_bEnabled = b; }
...
};
К>Заведомо не лучше. Невозможно будет использовать неявное преобразование типов.
Проверял на GCC 3.1. Там все нормально. Проходит без ругательств. В том числе и неявное преобразование типов с операторами возвращающими ссылку на константу. Вообще то я не совсем понимаю, почему не проходит неявное преобразование с ссылками на константы, а с обычными проходит? Приведи пример, пожалуйста. Просто я стараюсь делать как можно более похожие интерфейсы функций/операторов для константного и неконстантного объекта. Привычка.
К>Если определен operator T() const К>то нужда у них отпадает.
Угу. Сорри.
Здравствуйте, Atilla, Вы писали:
C>>А как же все остальные? сравнение, сложение, сдвиги, инкрементация и т.п. и т.д.? A>это для простеньких-то типов (int, char, void*) ?
А зачем их дискриминировать? Чем они хуже?
C>>>inline operator T const &() const { return t_; }
К>>Заведомо не лучше. Невозможно будет использовать неявное преобразование типов.
C>Проверял на GCC 3.1. Там все нормально. Проходит без ругательств. В том числе и неявное преобразование типов с операторами возвращающими ссылку на константу. Вообще то я не совсем понимаю, почему не проходит неявное преобразование с ссылками на константы, а с обычными проходит? Приведи пример, пожалуйста. Просто я стараюсь делать как можно более похожие интерфейсы функций/операторов для константного и неконстантного объекта. Привычка.
У меня тоже эта привычка. Однако такой пример:
Если определить оператор "значение" как
operator const T&() const
то имеем ошибки на всех операциях над значением: (компилятор VC6sp5)
class CChannel ...
{
...
auto_value<bool> m_bEnabled;
...
public:
STDMETHOD(notifyReceiver)()
{
if( !m_bEnabled )
// error C2675: unary '!' :
// 'class auto_value<bool,0>' does not define this operator or a conversion to a type
// acceptable to the predefined operator
...
}
...
};
(может быть, где-то в другом месте вылезет ambiguous call).
А пока я добавил эту тулзу вот с этими тремя операторами в большой проект, и надеюсь поиметь отзывы от сотрудников в течение недели.
К>(может быть, где-то в другом месте вылезет ambiguous call). К>А пока я добавил эту тулзу вот с этими тремя операторами в большой проект, и надеюсь поиметь отзывы от сотрудников в течение недели.
Кстати, если объявлены два таких оператора
inline operator T const &() const { return t_; }
inline operator T () const { return t_; }
Здравствуйте, comer, Вы писали:
C>Кстати, если объявлены два таких оператора C>
C>inline operator T const &() const { return t_; }
C>inline operator T () const { return t_; }
C>
C>то GCC 3.1, не может откомпилировать вот это:
Екандыбабай! А GCC 2.7.2 — сожрал (да, старый, да, кривой... но единственно доступный для целевой платформы).
Поскольку мне более всего важна совместимость со своей задачей, оставляю себе трех-операторный вариант. А дальше — поживем, увидим.
А безшаблонный вариант, интересно, как себя ведет?
Еще можно на Comeau-online проверить, но это — позже.
C>Интересно, кстати, какой из этих операторов в таком случае будет использовать VC?
Достаточно воткнуть трассирову, или же Александеску'вские compile-time check.
К> // доступ к данному
К> inline const T& data() const { return t_; }
К>
Саттер и ISO/IEC 14882 пишут, что функция вида:
T f();
где T является встроенным типом, всегда возвращает r-value. cv-квалифиатор не может быть применен к r-value, и это может помешать инстанцированию шаблонов.
Т.е., чтобы это было совсем корректно, возможно, придется воспользоваться type traits.
Здравствуйте, MaximE, Вы писали:
ME>Здравствуйте, Кодт, Вы писали:
К>>
К>> // доступ к данному
К>> inline const T& data() const { return t_; }
К>>
ME>Саттер и ISO/IEC 14882 пишут, что функция вида: ME>
ME>T f();
ME>
ME>где T является встроенным типом, всегда возвращает r-value. cv-квалифиатор не может быть применен к r-value, и это может помешать инстанцированию шаблонов. ME>Т.е., чтобы это было совсем корректно, возможно, придется воспользоваться type traits.
И именно поэтому я пишу не T data() const, а const T& data() const.
Чтобы получить константное l-value (и в дальнейшем, например, использовать указатель на константу:
Здравствуйте, Andrew S, Вы писали:
AS>Возможно, я скажу глупость, но я бы использовал template < class T, T i = T() > AS>что дает таки большую свободу в типах, нежели инициализирование нулем.
Вопрос: int() == 0?
А вообще, можно же сделать шаблон для инициализации POD-структур .
template< class POD >
struct auto_pod : public POD
{
auto_pod() { memset( static_cast<POD*>(this), 0, sizeof(POD) ); }
};
(тут нужно быть аккуратным с пустыми структурами, т.к. их sizeof() == 1).
Здравствуйте, Кодт, Вы писали:
К>И именно поэтому я пишу не T data() const, а const T& data() const. К>Чтобы получить константное l-value (и в дальнейшем, например, использовать указатель на константу:
Здравствуйте, Блудов Павел, Вы писали:
К>>(тут нужно быть аккуратным с пустыми структурами, т.к. их sizeof() == 1).
БП>Этот вопрос решается простым наследованием структуры с одним полем байтовой длины:
БП>Если POD без полей, то 1 == sizeof(POD), и 0 == _offsetof(_PODSize, _PODSize::_Dummy).
Хотя было бы полезно написать шаблон приблизительно такого вида:
template< class T >
struct struct_traits
{
.....
enum { isNotEmptyPOD = ..... };
.....
template < bool b >
class _auto_pod_impl : public T
{ };
template <>
class _auto_pod_impl<false> // cпециальная версия для структур с нулевым размером
: public T // для возможности преобразования типов
{
_auto_pod_impl() {}
_auto_pod_impl(const T&) {}
void operator=(const T&) {}
};
typedef _auto_pod_impl< isNotEmptyPOD > auto_pod;
};
template< class T >
class auto_pod : struct_traits<T>::auto_pod
{ };
Такой код может быть написан только в реализации стандартной библиотеки, или в компиляторно-зависимой библиотеке. Для всего остального разыменование нулевого указателя не является легальным C++ (p->m => (*(p)).m (см. 5.2.5/3)).
Ну, и про идентификаторы тоже... Все, что начинается с _<Большая латинская> — зарезервировано.