Проблема экземпляра класса шаблона C ++ - PullRequest
0 голосов
/ 05 июня 2018
#include <iostream>
#include <string>

using namespace std;

template<class T> class Sample {
private:
 T val;
public:
 Sample(T InitialVal=T()) : val(InitialVal)
 {
    // do nothing
 }
 ~Sample() 
 {
    // do nothing
 }
 void PrintVal(void)
 {
     try {
    cout << "[" << val << "]" << endl;
     } catch(...) {
        cout << "exception thrown" << endl;
     }
 }
};

int main() {
    // your code goes here
    Sample<int> ints(20), intd;
    Sample<char *> chars(const_cast<char*>("Neelakantan")), charsd;
    Sample<string> s, ss("neel");

    ints.PrintVal();
    intd.PrintVal();

    chars.PrintVal();
    charsd.PrintVal(); // <<- Culprit line. Commenting out this line works as expected.

    s.PrintVal();
    ss.PrintVal();

    return 0;
}

Когда я запускаю приведенный выше код, я получаю следующий вывод:

sh-4.4$ g++ -o main *.cpp                                                                                                                                                       
sh-4.4$ main                                                                                                                                                                    
[20]                                                                                                                                                                            
[0]                                                                                                                                                                             
[Neelakantan]                                                                                                                                                                   
[sh-4.4$

Когда я закомментирую строку "charsd.PrintVal();", я получаю следующий вывод:

[sh-4.4$ g++ -o main *.cpp                                                                                                                                                      
sh-4.4$ main                                                                                                                                                                    
[20]                                                                                                                                                                            
[0]                                                                                                                                                                             
[Neelakantan]                                                                                                                                                                   
[]                                                                                                                                                                              
[neel]                                                                                                                                                                          
sh-4.4$

В чем проблема с объектом 'charsd' экземпляра шаблона типа Sample ?Исключений не было.

версия компилятора:

sh-4.4$ g++ --version                                                                                                                                                           
g++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)                                                                                                                                      
Copyright (C) 2017 Free Software Foundation, Inc.                                                                                                                               
This is free software; see the source for copying conditions.  There is NO                                                                                                      
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                                                                                                     

sh-4.4$

Ответы [ 3 ]

0 голосов
/ 05 июня 2018

[Пересмотренный ответ основан на новой информации / знаниях.См. Также ответ Джонатана Уэйкли (я обновил его до того, как увидел это), который является автором gcc / libstdc ++ и кто воспользовался отчетом об ошибках, который я отправил.Большое спасибо ему за прямые и искренние извинения за абсурдное предположение, что ostream будет когда-либо вести себя так, как я описывал вначале.]

Что вы на самом деле делаете, на данный моментПо сути, это означает следующее:

std::cout << (const char *) nullptr << std::endl;

И то, что большинство людей ожидают получить (хотя, строго говоря, поведение того, что вы там делаете, не определено), будет SEGFAULT.

libstdc++, однако, который содержит реализацию ostream, которую использует gcc, делает что-то другое.Он устанавливает badbit в потоке, и , только если вы включили badbit exception (которые по умолчанию отключены), он (также) выдает исключение.Спасибо Джонатану Уэйкли за то, что он указал мне на это, когда я отправил отчет об ошибке, я ничего об этом не знал (и, очевидно, никто не публиковал сообщения в этой теме), когда я опубликовал первую версию этого ответа.

Но ваш код не включил указанные исключения, поэтому все, что происходит, это то, что badbit устанавливается на cout, и все последующие записи впоследствии молча завершаются неудачей.Ранее я неверно истолковал это как программу, тихо завершающую работу при передаче nullptr, но я ошибся, и я извиняюсь перед разработчиками за такое необоснованное предположение.Об этом подробнее говорится в комментариях.

Итак, чтобы получить исключение, когда это происходит, вы должны включить исключение badbit в потоке, что вы можете сделать так:

std::cout.exceptions (std::ostream::badbit | std::ios::failbit);

Тогда вы получите исключение, на которое надеялись.Лично мне не очень нравится такое поведение, я бы предпочел SEGFAULT (и на самом деле, с clang, вы делаете ), но Джонатан говорит мне, что так было с 2002 года, и разработчикиесть веские причины не менять его сейчас.

Появилась новая live demo , показывающая поведение gcc с добавленной выше строкой, и теперь вывод:

[20]
[0]
[Neelakantan]
terminate called after throwing an instance of 'std::__ios_failure'
  what():  basic_ios::clear: iostream error
[
Aborted

Так что, будьте осторожны, ловушка для неосторожных лежит в ожидании: «Почему ни одно из моих заявлений о регистрации не появляется внезапно?», Или что-то в этом роде.

0 голосов
/ 13 июня 2018

charsd содержит нулевой указатель, а запись нулевого const char* в ostream является неопределенным поведением.

Реализация стандартной библиотекой проверки GCC для этого условия (см. кодздесь ) и устанавливает флаг badbit в состоянии потока.badbit указывает на то, что состояние потока было повреждено, в этом случае неожиданным нулевым указателем, который, согласно стандарту, является неопределенным поведением.NB. Эта обработка нулевых указателей для ostream не документирована и может измениться в будущем.

Программа продолжает выполняться до завершения, но поскольку badbit установлен в потоке, все последующие записи завершаются неудачно,больше ничего не выводится на std::cout.

Программа не просто молча завершается, когда нулевой указатель записывается в поток .Он устанавливает флаг ошибки и продолжает работать.

Если вы хотите обнаружить, что вы можете это сделать, например, проверив состояние потока в конце main и напечатав сообщение со стандартной ошибкой:

int main() {
    // ...

    if (!std::cout)
        std::cerr << "An error happened while writing to cout\n";

    return 0;
}

Или, сообщая потоку cout, что вы хотите, чтобы ошибки превращались в исключения:

int main() {

    std::cout.exceptions(std::ios::badbit | std::ios::failbit);

    // ...

    return 0;
}

Теперь программа выдаст исключение, как только ошибка произойдет:

terminate called after throwing an instance of 'std::ios_base::failure[abi:cxx11]'
  what():  basic_ios::clear: iostream error
Aborted (core dumped)
0 голосов
/ 05 июня 2018

Проблема в том, что поле val; объекта charsd инициализируется нулевым указателем.Поэтому попытка передать его в operator << нарушает предусловие оператора и вызывает неопределенное поведение.

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