如何在C++中显式调用异常抛出方法?

aby*_*s.7 22 c++ c++14

我有一个简单的课程:

class A {
 public:
  bool f(int* status = nullptr) noexcept {
    if (status) *status = 1;
    return true;
  }
  void f() {
    throw std::make_pair<int, bool>(1, true);
  }
};

int main() {
  A a;
  a.f(); // <- Ambiguity is here! I want to call 'void f()'
}
Run Code Online (Sandbox Code Playgroud)

我想通过任何方式解决方法调用的歧义,转而使用异常抛出方法.

这种界面背后的基本原理:

  • 拥有noexcept(true)noexcept(false)界面,
  • 允许通过noexcept(false)变量中的指针选择性地获取额外信息- 而noexcept(true)变体将始终将此信息打包在异常中.

有可能吗?建议您建立更好的界面.

bol*_*lov 41

你已经发现,拥有这种签名的功能显然是一个糟糕的设计.真正的解决方案是为它们设置不同的名称或丢失默认参数,并已在其他答案中提供.

但是,如果你坚持使用界面,你不能改变或只是为了它的乐趣,这里是你如何明确地调用void f():

诀窍是使用函数指针转换来解决歧义:

a.f(); // <- ambiguity is here! I want to call 'void f()'

(a.*(static_cast<void (A::*)()>(&A::f)))(); // yep... that's the syntax... yeah...
Run Code Online (Sandbox Code Playgroud)

好的,所以它有效,但不要写这样的代码!

有一些方法可以使它更具可读性.

使用指针:

// create a method pointer:
auto f_void = static_cast<void (A::*)()>(&A::f);

// the call is much much better, but still not as simple as `a.f()`
(a.*f_void)();
Run Code Online (Sandbox Code Playgroud)

创建lambda或自由函数

auto f_void = [] (A& a)
{
    auto f_void = static_cast<void (A::*)()>(&A::f);
    (a.*f_void)();
};

// or

void f_void(A& a)
{
    auto f_void = static_cast<void (A::*)()>(&A::f);
    (a.*f_void)();
};


f_void(a);
Run Code Online (Sandbox Code Playgroud)

我不知道这是否有必要更好.调用语法肯定更简单,但是当我们从方法调用语法切换到自由函数调用语法时,它可能会令人困惑.

  • 唯一的答案是回答问题而不是质疑设计. (20认同)
  • @WorldSEnder有时质疑设计是正确的答案.另见[敲钉子:旧鞋子或玻璃瓶?](https://weblogs.asp.net/alex_papadimoulis/408925) (17认同)
  • @WorldSEnder:问题明确指出:*"欢迎提供更好界面的建议."* (6认同)

Gui*_*let 28

两个版本f都有不同的含义.

它们应该有两个不同的名称,如:

  • f 对于投掷者来说,因为使用它意味着你对成功充满信心,失败将成为程序中的一个例外.
  • try_f()或者tryF()基于错误返回的,因为使用它意味着调用失败是预期的结果.

两个不同的含义应该在设计中反映出两个不同的名称.


dav*_*mac 15

因为对我来说这似乎是显而易见的,我可能会遗漏某些东西或者可能无法完全理解你的问题.但是,我认为这完全符合您的要求:

#include <utility>

class A {
 public:
  bool f(int* status) noexcept {
    if (status) *status = 1;
    return true;
  }
  void f() {
    throw std::make_pair<int, bool>(1, true);
  }
};

int main() {
  A a;
  a.f(); // <- now calls 'void f()'
  a.f(nullptr);  // calls 'bool f(int *)'
}
Run Code Online (Sandbox Code Playgroud)

我只是从noexcept变体中删除了默认参数.它仍然可以noexcept通过nullptr作为参数传递来调用变体,这似乎是一种非常好的方式,表明你想要调用该函数的特定变体 - 毕竟,必须有一些语法标记来指示你想要的变体打电话!


Chr*_*ckl 5

我同意其他用户的建议,只需删除默认参数.

支持这种设计的一个有力论据是,它将与新的C++ 17文件系统库保持一致,其功能通常为调用者提供异常和错误引用参数之间的选择.

例如std::filesystem::file_size,参见哪个有两个重载,其中一个是noexcept:

std::uintmax_t file_size( const std::filesystem::path& p );

std::uintmax_t file_size( const std::filesystem::path& p,
                          std::error_code& ec ) noexcept;
Run Code Online (Sandbox Code Playgroud)

除了默认参数之外,这个设计背后的想法(最初来自Boost.Filesystem)几乎与你的相同.删除它,你就像标准库的一个全新的组件,显然可以预期不会有完全破坏的设计.