参数类型的函数具有选择的非常量引用的复制构造函数?

发布于 2024-10-12 03:07:09 字数 1013 浏览 1 评论 0原文

不久前,当我想编写 is_callable 特征时,我对某些代码的以下行为感到困惑。重载解析不会调用接受非常量引用参数的函数,对吧?为什么它在下面不拒绝,因为构造函数需要一个 Test& ?我预计它需要 f(int)

struct Test {
  Test() { }

  // I want Test not be copyable from rvalues!
  Test(Test&) { }

  // But it's convertible to int
  operator int() { return 0; }
};

void f(int) { }
void f(Test) { }

struct WorksFine { };
struct Slurper { Slurper(WorksFine&) { } };
struct Eater { Eater(WorksFine) { } };

void g(Slurper) { }
void g(Eater) { } // chooses this, as expected

int main() {
  // Error, why?
  f(Test());

  // But this works, why?
  g(WorksFine());
}

错误消息是

m.cpp: In function 'int main()':
m.cpp:33:11: error: no matching function for call to 'Test::Test(Test)'
m.cpp:5:3: note: candidates are: Test::Test(Test&)
m.cpp:2:3: note:                 Test::Test()
m.cpp:33:11: error:   initializing argument 1 of 'void f(Test)'

您能解释一下为什么一个有效但另一个无效吗?

Some time ago I was confused by the following behavior of some code when I wanted to write a is_callable<F, Args...> trait. Overload resolution won't call functions accepting arguments by non-const ref, right? Why doesn't it reject in the following because the constructor wants a Test&? I expected it to take f(int)!

struct Test {
  Test() { }

  // I want Test not be copyable from rvalues!
  Test(Test&) { }

  // But it's convertible to int
  operator int() { return 0; }
};

void f(int) { }
void f(Test) { }

struct WorksFine { };
struct Slurper { Slurper(WorksFine&) { } };
struct Eater { Eater(WorksFine) { } };

void g(Slurper) { }
void g(Eater) { } // chooses this, as expected

int main() {
  // Error, why?
  f(Test());

  // But this works, why?
  g(WorksFine());
}

Error message is

m.cpp: In function 'int main()':
m.cpp:33:11: error: no matching function for call to 'Test::Test(Test)'
m.cpp:5:3: note: candidates are: Test::Test(Test&)
m.cpp:2:3: note:                 Test::Test()
m.cpp:33:11: error:   initializing argument 1 of 'void f(Test)'

Can you please explain why one works but the other doesn't?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

幽蝶幻影 2024-10-19 03:07:09

重载解析选择与所提供的参数最匹配的函数。您提供了一个测试。无需转换——使用身份转换。因此函数解析选择f(Test)。无法从您提供的右值复制测试,但重载解析已成功...从不检查到 int 的转换。

选择 g(Eater) 是因为类型不完全匹配,不使用恒等转换,并且编译器必须找到有效的转换例程。 g(Slurper) 不会,因为您无法从提供的参数中得出一个结果。

“为什么这个没有失败:struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }"

因为 f(A&) 对于所提供的参数来说不是可行的重载。在这种情况下,参数是一个引用,并且临时值不绑定到非常量这一事实可以影响解析。在这种情况下,它确实如此,并且该函数的该版本成为非候选版本,只留下一个可以运行的版本。

Overload resolution picks the function that is the closest match to the supplied argument. You supplied a Test. No conversion necessary -- identity conversion used. Thus function resolution chooses f(Test). Test can't be copied from rvalue, which you supplied, but overload resolution has succeeded already...conversion to int is never checked.

g(Eater) is chosen because the types don't exactly match, the identity conversion is NOT used, and the compiler has to find a conversion routine that works. g(Slurper) doesn't because you can't make one out of the supplied argument.

"Why doesn't this one fail: struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }"

Because f(A&) is not a viable overload for the supplied argument. In this case the parameter is a reference and the fact that temps don't bind to non-const is allowed to effect the resolution. In this case it does and the that version of the function becomes a non-candidate, leaving only the one and it works.

月竹挽风 2024-10-19 03:07:09

基本上,对于重载决策,假设类型 A 的对象可以转换为类型 A 的对象,而不管两者中任何一个的 cv 限定如何。

来自草案 n1905:

13.3.3.1:重载。重载解决方案。最佳可行函数。隐式转换序列

6 当参数类型不是引用时,隐式转换序列会根据参数表达式对参数进行复制初始化建模。隐式转换序列是转换参数所需的序列
表达式为参数类型的右值。 [ 注意:当参数具有类类型时,这是一个概念性的
为第 13 条的目的而定义的转换;实际的初始化是根据构造函数定义的,而不是
一个转换。 — 尾注 ] 顶级简历资格中的任何差异都包含在初始化本身中,并且不会
不构成转换。
[示例:A 类型的形参可以从 const A 类型的实参初始化。
这种情况的隐式转换序列是恒等序列;它不包含从 const A 到
A. — 结束示例 ] 当参数具有类类型并且参数表达式具有相同类型时,隐式
转换序列是身份转换。 [...]


Basically, for overload resolution, it is assumed that an object of type A can be converted to an object of type A regardless of any cv-qualification on either of the two.

From draft n1905:

13.3.3.1: Overloading.Overload Resolution.Best Viable Function.Implicit Conversion Sequences

6 When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression. The implicit conversion sequence is the one required to convert the argument
expression to an rvalue of the type of the parameter. [ Note: when the parameter has a class type, this is a conceptual
conversion defined for the purposes of clause 13; the actual initialization is defined in terms of constructors and is not
a conversion. — end note ] Any difference in top-level cv-qualification is subsumed by the initialization itself and does
not constitute a conversion.
[ Example: a parameter of type A can be initialized from an argument of type const A.
The implicit conversion sequence for that case is the identity sequence; it contains no “conversion” from const A to
A. — end example ] When the parameter has a class type and the argument expression has the same type, the implicit
conversion sequence is an identity conversion
. [...]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文