const-правильность и идиома безопасного bool - PullRequest
9 голосов
/ 11 августа 2011

У меня есть еще один вопрос, связанный с идиомой безопасности bool:

typedef void (Testable::*bool_type)() const;             // const necessary?
void this_type_does_not_support_comparisons() const {}   // const necessary?

operator bool_type() const
{
    return ok_ ? &Testable::this_type_does_not_support_comparisons : 0;
}

Почему bool_type (typedef) и this_type_does_not_support_comparisons равны const? Никто не должен на самом деле вызывать функцию-член через указатель возврата в любом случае, верно? const нужен здесь? В противном случае operator bool_type (функция-член) нарушит правильность const?

Ответы [ 2 ]

5 голосов
/ 11 августа 2011

«Безопасная идиома» - это технический ответ на вопрос «я хочу автомобиль, который будет как спортивным, так и тракторным, и, возможно, лодкой». Практический ответ - это не технический ответ & hellip;

Тем не менее, проблема, которую он решает, состоит в том, чтобы просто дать результат, который можно преобразовать в bool, но не во что-либо еще (в противном случае экземпляр класса может быть передан в качестве фактического аргумента, например, формальным аргументом был int, скажем). Указатель данных может быть преобразован в void*. Указатель на функцию не является, по крайней мере, формально в стандарте C ++ (Posix - это нечто иное, также практикуйтесь).

Использование указателя на функцию-член защищает от случайного вызова функции, учитывая указатель от оператора безопасной bool. const немного ограничивает его, но если судьба поставила кого-то на путь совершения максимального количества глупых ошибок, этому человеку все равно удастся вызвать функцию "ничего не делать". Вместо const я думаю, я бы просто позволил ему иметь аргумент закрытого типа, где другой код не может предоставить такой аргумент, и тогда он больше не должен быть глупым типом функции-члена.

Может выглядеть так:

#include <stdio.h>

class Foo
{
private:
    enum PrivateArg {};
    typedef void (*SafeBool)( PrivateArg );
    static void safeTrue( PrivateArg ) {}

    bool    state_;

public:
    Foo( bool state ): state_( state ) {}

    operator SafeBool () const
    { return (state_? &safeTrue : 0); }
};

int main()
{
    if( Foo( true ) ) { printf( "true\n" ); }
    if( Foo( false ) ) { printf( "false\n" ); } // No output.

    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}

Конечно, практический ответ - что-то вроде этого:

#include <stdio.h>

class Foo
{
private:
    bool    isEmpty_;

public:
    Foo( bool asInitiallyEmpty )
        : isEmpty_( asInitiallyEmpty )
    {}

    bool isEmpty() const { return isEmpty_; }
};

int main()
{
    if( Foo( true ).isEmpty() ) { printf( "true\n" ); }
    if( Foo( false ).isEmpty() ) { printf( "false\n" ); } // No output.

    //bool const x0 = Foo( false );       // No compilado!
    //int const x1 = Foo( false );        // No compilado!
    //void* const x2 = Foo( false );      // No compilado!
}

Резюме по отношению к задаваемые вопросы:

  • Почему константные сравнения типа bool_type (typedef) и this_type_does_not_support_const?

Кто-то не совсем понял, что они закодировали. Или возможно они намеревались немного ограничить возможность звонить. Но тогда, довольно бесполезная мера.

  • Никто не должен фактически вызывать функцию-член через указатель возврата, верно?

правый.

  • Требуется ли здесь const?

номер

  • В противном случае оператор bool_type (функция-член) нарушил бы правильность const?

номер

Приветствия и hth.,

1 голос
/ 11 августа 2011

8.3.5 / cv-qualifier-seq должен быть только частью типа функции для нестатической функции-члена, типа функции, к которому относится указатель на член, или типа функции верхнего уровняобъявление функции typedef.Эффект cv-qualifier-seq в объявителе функции отличается от добавления cv-qualification поверх типа функции, т. Е. Он не создает cv-квалифицированный тип функции.

Если я правильно прочитал, вы можете вернуть указатель на неконстантный член в функции константного члена.Вы просто не сможете вызывать его с неконстантным объектом.

Способ запрета вызова:

private:
    struct private_ 
    {
        void this_type_does_not_support_comparisons() {}
    };

public:
    typedef void (private_::*bool_type)() const;

    operator bool_type() const
    {
        return ok_ ? &private_::this_type_does_not_support_comparisons : 0;
    }

По-прежнему можно сравнивать указатель на функции-члены на равенство.Вы должны написать operator== и operator!= для Testable::bool_type типов, которые вызывают ошибку.Проще сделать это с CRTP-формой безопасной bool-идиомы, поскольку эти операторы становятся шаблонами и, следовательно, могут иметь ошибочное тело.

Пример:

template <typename T>
class safe_bool_concept
{
    // Implementation detail of safe bool
protected:
    ~safe_bool_concept() {}

public:
    operator safe_bool() const
    {
        return static_cast<const T*>(this)->is_null() ? ...;
    }
};

struct Foo : safe_bool_concept<Foo>
{
    ...

private:
    friend class safe_bool_concept<Foo>;
    bool is_null() const { ... }
};

тогда вы можете сделать (сделать то же самоес !=):

template <typename T>
void operator==(const safe_bool_concept<T>& x, const safe_bool_concept<T>&)
{
    x.some_private_member(); // invalid, but won't be generated
                             // unless safe_bool classes are compared
}

Это означает, что идиома безопасного bool должна быть реализована через CRTP, если вы хотите запретить сравнения.Однако сравнение с нулем все равно будет работать.

Если вы пойдете по пути, не являющемуся членом функции, вам придется предоставить также <, >, <= и >=.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...