C++ Как определить, когда nullptr передается функции, где ожидается std::string?

Я не мог найти темы, дающие четкий ответ на этот вопрос -

у меня есть конструктор, например:

FanBookPost::FanBookPost(Fan* owner, std::string content);

Fan — это еще один класс в моем коде, но проблематичным является контент:

поскольку std::string не является указателем, я ожидаю, что вызов конструктора с помощью (fan, nullptr) будет невозможен. однако он компилируется! ... и вылетает во время выполнения с EXC_BAD_ACCESS.

этого можно избежать?


person Zephyer    schedule 21.01.2014    source источник
comment
Передать строку по ссылке. FanBookPost::FanBookPost(Fan* owner, std::string &content);   -  person smac89    schedule 22.01.2014
comment
разрешен как законный контент @Smac89   -  person Zephyer    schedule 22.01.2014
comment
Если вы действительно этого хотите, вы можете перегрузить его версией const char *, которая пересылает, если не является нулевой.   -  person chris    schedule 22.01.2014
comment
Сбой произойдет при вызове конструктора std::string, это не проблема вашего кода.   -  person Sebastian Hoffmann    schedule 22.01.2014
comment
Вы можете перегрузить его функцией, которая принимает std::nullptr_t. Константы ненулевого указателя не будут выбирать эту перегрузку.   -  person dyp    schedule 22.01.2014
comment
@chris, я собираюсь сделать перегрузку, чтобы быть в безопасности. Вы можете опубликовать это как комментарий, чтобы я мог принять ответ? всем остальным тоже спасибо за ответы   -  person Zephyer    schedule 22.01.2014
comment
stackoverflow.com/ вопросы/10771864/   -  person marcinj    schedule 22.01.2014
comment
@ShafikYaghmour Нулевой указатель — это вполне допустимый указатель, char или другой. Единственная проблема в том, что конструктор std::string не принимает его.   -  person    schedule 22.01.2014
comment
@dyp Хотя это не предотвратит такие вещи, как char *c = 0; FanBookPost fbp(..., c); или даже FanBookPost fbp(..., 0);.   -  person    schedule 22.01.2014
comment
@dyp, хорошо. Мне это больше нравится.   -  person chris    schedule 22.01.2014
comment
@delnan Верно, но это более явно ошибочное ИМХО (т. Е. Более очевидно, что char* будет интерпретироваться как строка и должно указывать на массив с нулевым завершением).   -  person dyp    schedule 22.01.2014
comment
@delnan 0 — это константа нулевого указателя.   -  person dyp    schedule 22.01.2014
comment
Однако @dyp 0 не относится к типу nullptr_t.   -  person Mark B    schedule 22.01.2014
comment
@MarkB Но преобразование в nullptr_t является стандартным преобразованием, тогда как преобразование в string является преобразованием, определяемым пользователем. Стандартное преобразование лучше, поэтому перегрузка nullptr_t предпочтительнее.   -  person dyp    schedule 22.01.2014
comment
@dyp Уже существующий char *, являющийся нулевым, гораздо менее явно ошибочен, чем передача nullptr дословно. Приоритет перегрузки RE: Интересно, спасибо за указание!   -  person    schedule 22.01.2014
comment
@delnan Эту ошибку сложнее обнаружить, я согласен, но когда вы передаете char*, я думаю (по крайней мере, более) разумно предположить, что она должна быть действительной.   -  person dyp    schedule 22.01.2014
comment
@dyp Я не совсем понимаю твою точку зрения. Да, некоторые случайные char * имеют более высокую (а именно › 0%) вероятность того, что они не будут нулевыми, но когда они равны нулю, гораздо сложнее определить, как и почему. Кроме того, учитывая, что многие люди (включая меня) очень склонны кодировать для счастливого случая, игнорируя возможность того, что указатели будут нулевыми, если это не суют им в лицо, видя f(c_str), они не будут проверять, принимает ли (1) f значение null или ( 2) c_str не может быть нулевым. Напротив, f(nullptr) затрудняет игнорирование возможности нулевого указателя.   -  person    schedule 22.01.2014
comment
@delnan Мое замечание касалось не обнаружения ошибок, а предположения о том, что происходит. Когда я вижу компиляцию f(nullptr), я более склонен предполагать, что f может обрабатывать нулевые указатели, чем когда я вижу, что это не удается, но компилируется (только) f(c). Конечно, проверка нулевых указателей разумна и полезна в интерфейсе (в основном, когда вы не хотите полагаться на то, что пользователь интерфейса сохранит гарантию отсутствия nullptr).   -  person dyp    schedule 22.01.2014


Ответы (4)


Проблема здесь в том, что сбой произойдет при вызове конструктора std::string (поскольку там обращается nullptr, интерпретируемый как const char*). Здесь вы ничего не можете сделать против этого, кроме как сказать другим народам не делать дерьма. Это не проблема вашего конструктора и, следовательно, не ваша ответственность (кроме того, что вы не можете это предотвратить).

person Sebastian Hoffmann    schedule 21.01.2014

Вы наблюдаете, что вы можете неявно создать std::string из указателя символа (в данном случае nullptr), который затем передается функции. Однако создание string из нулевого указателя не допускается. В подписи вашего метода нет ничего плохого, просто использование клиента нарушает контракт конструктора std::string.

person Mark B    schedule 21.01.2014

Как насчет использования типа прокси/обертки, если вы действительно хотите быть в безопасности:

template<typename T>
struct e_t
{
public:
    inline e_t ( e_t const & other )
        : m_value( other.m_value )
    {}

    inline T & value( void )                { return m_value; }
    inline operator T&()                    { return m_value;   }
    inline e_t( const T& c ) : m_value( c )     {}

private:
    T m_value;
};

void FanBookPost(int* owner, e_t<std::string> content) {
}

int main()
{
    int n = 0;
    //FanBookPost(&n, 0); // compiler error
    //FanBookPost(&n, nullptr); // compiler error
    //FanBookPost(&n, ""); // unfortunately compiler error too
    FanBookPost(&n, std::string(""));
}
person marcinj    schedule 21.01.2014

Проблема в том, что std::string имеет неявный ctor, который принимает char * в качестве единственного (обязательного) параметра. Это дает неявное преобразование из nullptr в std::string, но дает неопределенное поведение, потому что этот ctor специально требует ненулевого указателя.

Есть несколько способов предотвратить это. Вероятно, наиболее эффективным было бы взять (неконстантную) ссылку на std::string, что потребует передачи (невременного) string в качестве параметра.

FanBookPost::FanBookPost(Fan* owner, std::string &content);

Это должно привести к нежелательному побочному эффекту предоставления функции возможности изменять переданную строку. Это также означает, что (с соответствующим компилятором1) вы не сможете передать nullptr или строковый литерал в функцию — вам придется передать фактический экземпляр std::string.

Если вы хотите иметь возможность передавать строковый литерал, вы можете добавить перегрузку, которая принимает параметр char const *, и, возможно, также принимает параметр nullptr_t. Первый будет проверять наличие ненулевого указателя перед созданием строки и вызовом функции, которая берет ссылку на строку, а второй будет делать что-то вроде регистрации ошибки и безусловного завершения программы (или, возможно, протоколировать ошибку). и сгенерировать исключение).

Это раздражает и неудобно, но может улучшить текущую ситуацию.


  1. К сожалению, в последний раз, когда я заметил, что MS VC++ не соответствует требованиям в этом отношении. Это позволяет передавать временный объект по неконстантной ссылке. Обычно это довольно безвредно (он просто позволяет вам модифицировать временное, но обычно не имеет видимых побочных эффектов). В этом случае это гораздо более проблематично, поскольку вы зависите от него специально, чтобы предотвратить передачу временного объекта.
person Jerry Coffin    schedule 21.01.2014
comment
Я бы рекомендовал использовать /Za (отключить языковые расширения). - person dyp; 22.01.2014