C ++模板类实例问题

时间:2018-06-05 09:30:47

标签: c++ templates

#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$

Sample&lt;类型的模板实例的'charsd'对象有什么问题? char *&gt; ?没有例外被抛出。

编译器版本:

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 :(得分:5)

问题是val;对象的字段charsd是用空指针初始化的。因此,尝试将其传递给operator <<违反运算符前置条件并导致未定义的行为。

答案 1 :(得分:3)

charsd包含空指针,将const char*写入ostream是未定义的行为。

GCC的标准库实现检查该条件(请参阅the code here)并在流状态中设置badbit标志。 badbit表示流状态已损坏,在这种情况下是标准所说的未定义行为的意外空指针。注:对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)

答案 2 :(得分:1)

[基于新信息/知识的修订答案。另请参阅Jonathan Wakeley的回答(我在看到之前对此进行了更新),谁是gcc / libstdc ++撰稿人,并且接受了我提交的错误报告。非常感谢他让我直截了当,最诚挚的抱歉做出了荒谬的假设ostream将以我之前描述的方式表现出来。]

在失败时,你实际上在做什么,实际上是这样的:

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

大多数人都希望得到的(尽管严格来说,你所做的事情的行为是未定义的)将是一个SEGFAULT。

然而,

libstdc++,包含ostream使用的gcc的实现,它有所不同。它在流上设置badbit,只有在您启用badbit例外(默认情况下已禁用)时才设置,它(也)会抛出异常。感谢Jonathan Wakeley在我提交有关此问题的错误报告时向我指出这一点,当我发布此答案的第一个版本时,我对此一无所知(显然也没有其他人发布到此主题)。

但是你的代码没有启用所述异常,所以所有发生的事情都是badbitcout上设置,所有后续写入随后无声地失败。我以前误解了这个程序,当nullptr被传入时程序悄然退出,但我错了,我向开发人员道歉,做出这样一个无根据的假设。在评论中还有更多相关内容。

因此,要在发生这种情况时抛出异常,您必须在流上启用badbit异常,您可以这样做:

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

然后你得到了你所希望的例外。就我个人而言,我不喜欢这种行为,我更喜欢SEGFAULT(事实上,还有clang,you do),但Jonathan告诉我它就是那样的自2002年以来,开发人员有充分的理由不改变它。

有一个新的live demo显示gcc的行为,并添加了上面的行,现在是输出:

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

所以,请注意,一个疏忽的陷阱在于等待那里,&#34;为什么我的日志记录都没有突然出现?&#34;或类似的东西。