重构代码以在编译时断言而不是在运行时抛出异常

问题描述

考虑下面的代码示例,一个带有基本重载算术运算符的简单模板类包装器。在此类的 operator/ 中,如果检测到除以 0,我将使用三元运算符抛出异常,否则我将返回计算结果。

some.h

#pragma once

#include <cassert>
#include <stdexcept>

template<typename T>
struct Var {
    T var;
   
    // ... other operators
 
    const auto operator/(const Var& rhs) {        
        return ((rhs.var == 0) ? throw std::exception("Division by 0") : (var / rhs.var));
    }
};

这是驱动程序:

#include <iostream>

#include "some.h"

int main() {
    try {
        Var<int> t1{ 4 };
        Var<int> t2{ 0 };
        auto t3 = t1 / t2;
        std::cout << t3 << '\n';
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    } catch (...) {
        std::cerr << "Unknown Exception\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

这将编译和构建,当我们运行它时(我使用的是 Visual Studio),它会抛出一个异常,将此消息发送到控制台

Division by 0

C:\Users\...\source\repos\Data Structure Samples\x64\Debug\Data Structure Samples.exe (process 8756) exited with cod
e 1.
To automatically close the console when debugging stops,enable Tools->Options->Debugging->Automatically close the conso
le when debugging stops.
Press any key to close this window . . .

好的,这很简单,而且有效。


假设我想重构这段代码有两个主要目标:

  • 首先,从类中抽象出操作符
  • 不想抛出异常,而是在编译时断言。

这个类会变成这样:

some.h

#pragma once

#include <cassert>
#include <stdexcept>

template<typename T>
struct Var {
    T var;
};

// ... other operators

template<typename T>
const auto operator/(const Var<T>& lhs,const Var<T>& rhs) {
    static_assert(rhs.var != 0,"Division by 0!");

    return (lhs.var / rhs.var);
}

但是,由于这些生成的错误而无法编译...

1>------ Build started: Project: Data Structure Samples,Configuration: Debug x64 ------
1>main.cpp
1>c:\users\...\source\repos\data structure samples\data structure samples\datastructs.h(25): error C2131: expression did not evaluate to a constant
1>c:\users\...\source\repos\data structure samples\data structure samples\datastructs.h(25): note: failure was caused by a read of a variable outside its lifetime
1>c:\users\...\source\repos\data structure samples\data structure samples\datastructs.h(25): note: see usage of 'rhs'
1>c:\users\...\source\repos\data structure samples\data structure samples\main.cpp(15): note: see reference to function template instantiation 'const auto operator /<int>(const Var<int> &,const Var<int> &)' being compiled
1>Done building project "Data Structure Samples.vcxproj" -- FAILED.
========== Build: 0 succeeded,1 failed,0 up-to-date,0 skipped ==========

我可以很容易地在运行时使用 assert 而没有任何问题,但是,我正在努力弄清楚如何在这种情况下正确使用 static_assert。我做错了什么,我在这里错过了什么?我在网上搜索了 static_assert 的各种用例示例,但没有找到任何合适的。即使我将运算符移回类中并尝试使用 static_assert,我仍然会收到非常相似的错误消息。

解决方法

经过仔细研究,我找到了一个可行的解决方案。断言或抛出异常都不会导致程序中止。

但是,在以后的计算中使用它时会有一点担心......这将迫使我按照这些术语编写其余的源代码,并且必须检查所有内容以确保它不是{ {1}} 以防止任何进一步的计算在不退出应用程序的情况下提前中止。

话虽如此,这就是我所做的,这就是我的操作员现在的样子......

NaN

当我运行相同的程序时,#include <cmath> template<typename T> struct Var { T var; }; template<typename T> const auto operator/(const Var<T>& lhs,const Var<T>& rhs) { return (rhs.var == 0) ? nan("0") : (lhs.var / rhs.var); } 会打印到控制台。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...