C++ 如何在C++;?

C++ 如何在C++;?,c++,c++14,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 her

我有一个简单的课程:

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()'
}
A类{
公众:
bool f(int*status=nullptr)无异常{
如果(状态)*状态=1;
返回true;
}
void f(){
抛出std::生成_对(1,true);
}
};
int main(){
A A;

a、 两个版本的
f
有不同的含义

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

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

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

在C++14中,它是不明确的,因为
noexcept
不是函数签名的一部分。因此

您有一个非常奇怪的接口。尽管
f(int*status=nullptr)
被标记为
noexcept
,因为它有一个确实引发异常的孪生兄弟,所以您并没有真正为调用方提供逻辑异常保证。似乎您同时希望
f
在不满足先决条件时总是成功地引发异常(status有一个有效值,即not
nullptr
).但是如果
f
抛出,对象处于什么状态?你看,你的代码很难推理


我建议你看一看。它会告诉读者你实际上想做什么。

因为这对我来说基本上是显而易见的,我可能遗漏了什么或者可能没有完全理解你的问题。但是,我认为这正是你想要的:

#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 *)'
}
#包括
甲级{
公众:
布尔f(整数*状态)无例外{
如果(状态)*状态=1;
返回true;
}
void f(){
抛出std::生成_对(1,true);
}
};
int main(){
A A;

a、 f();//我同意其他用户的建议,只是删除默认参数

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

例如,请参见,它有两个重载,其中一个是
noexcept


此设计背后的思想(最初来自Boost.Filesystem)除了默认参数外,它与您的几乎相同。删除它,您就可以像标准库的一个全新组件一样进行操作,这显然不会有完全损坏的设计。

您已经发现,具有此类签名的函数显然是一个糟糕的设计。真正的解决方案是使用不同的t为它们命名,否则将丢失默认参数,并且已在其他答案中提供

但是,如果您被一个无法更改的接口卡住了,或者只是为了好玩,下面是如何显式调用
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...

a.f();//那么,如果代码没有准备好返回错误,您是否尝试抛出异常

那么,怎么样

class ret
{
    bool success;
    mutable bool checked;
    int code;
public:
    ret(bool success, int code) : success(success), checked(false), code(code) { }
    ~ret() { if(!checked) if(!success) throw code; }

    operator void *() const { checked = true; return reinterpret_cast<void *>(success); }
    bool operator!() const { checked = true; return !success; }

    int code() const { return code; }
};
class-ret
{
成功;
可变布尔校验;
int代码;
公众:
ret(bool success,int code):success(success),checked(false),code(code){}
~ret(){if(!checked)if(!success)抛出代码;}
运算符void*()const{checked=true;返回reinterpret_cast(成功);}
布尔运算符!()常量{checked=true;返回!成功;}
int code()常量{返回代码;}
};
但这仍然是一个问题


通过删除析构函数中的
if(!success)
检查,您可以在不查看返回代码时抛出代码。

C++已经有一个类型专门用作参数,用于消除函数的抛出变量和非抛出变量之间的歧义:
std::nothrow\u t
。您可以使用它

#include <new>

class A {
 public:
  bool f(std::nothrow_t, 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(); // Calls 'void f()'
  a.f(std::nothrow); // Calls 'void f(std::nothrow_t, int*)'
}
#包括
甲级{
公众:
bool f(std::nothrow\u t,int*status=nullptr)无异常{
如果(状态)*状态=1;
返回true;
}
void f(){
抛出std::生成_对(1,true);
}
};
int main(){
A A;
a、 f();//调用'void f()'
a、 f(std::nothrow);//调用'void f(std::nothrow\u t,int*)'
}
尽管我仍然希望接口的名称能够区分变体,或者可能不需要区分变体。

这里是一个纯编译时方法。 如果编译器在优化函数指针调用时遇到问题,它可能会很有用

#include <utility>

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);
  }
};

template<void (A::*F)()>
struct NullaryFunction {
  static void invoke(A &obj) {
    return (obj.*F)();
  }
};

int main() {
  A a;
  // a.f(); // <- Ambiguity is here! I want to call 'void f()'
  NullaryFunction<&A::f>::invoke(a);
}
#包括
甲级{
公众:
bool f(int*status=nullptr)无异常{
如果(状态)*状态=1;
返回true;
}
void f(){
抛出std::生成_对(1,true);
}
};
模板
结构空函数{
静态无效调用(A&obj){
报税表(obj.*F)();
}
};
int main(){
A A;

//a.f();//如果我没记错的话,C++17将
noexcept
作为函数类型的一部分,如果你没有被困在C++14中,这可能会有所帮助。为不同的函数使用不同的名称。但是如果你绝对想要相同的名称接口,那么由于你有不同的签名,你可以简单地将
static\u cast
转换为相关的函数类型。这太难看了。难看是不好受的标志。从
noexcept
变量中删除默认参数,问题就解决了?你需要不同的名称。例如,在Android中,我们有很多
OrThrow()
方法用于
()
方法。答案不错,但第一句话有点误导。歧义不仅仅是因为
noexcept
不是函数签名的一部分。在C++17中,代码仍然失败,
noexcept
是函数签名的一部分,因为调用仍然是歧义的。这是一个好的设计决策,消除了错误呼叫站点上的任何歧义。呼叫方不需要依靠语言来推断正确的过载,
class ret
{
    bool success;
    mutable bool checked;
    int code;
public:
    ret(bool success, int code) : success(success), checked(false), code(code) { }
    ~ret() { if(!checked) if(!success) throw code; }

    operator void *() const { checked = true; return reinterpret_cast<void *>(success); }
    bool operator!() const { checked = true; return !success; }

    int code() const { return code; }
};
#include <new>

class A {
 public:
  bool f(std::nothrow_t, 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(); // Calls 'void f()'
  a.f(std::nothrow); // Calls 'void f(std::nothrow_t, int*)'
}
#include <utility>

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);
  }
};

template<void (A::*F)()>
struct NullaryFunction {
  static void invoke(A &obj) {
    return (obj.*F)();
  }
};

int main() {
  A a;
  // a.f(); // <- Ambiguity is here! I want to call 'void f()'
  NullaryFunction<&A::f>::invoke(a);
}