为啥我可以阻止基元而不是用户定义类型的隐式转换?

Posted

技术标签:

【中文标题】为啥我可以阻止基元而不是用户定义类型的隐式转换?【英文标题】:Why can I prevent implicit conversions for primitives but not user-defined types?为什么我可以阻止基元而不是用户定义类型的隐式转换? 【发布时间】:2017-05-15 21:55:45 【问题描述】:

高完整性 C++ 标准建议可以删除函数的右值参数,从而防止隐式转换。

http://www.codingstandard.com/rule/8-3-4-define-delete-functions-with-parameters-of-type-rvalue-reference-to-const/

我发现原语和用户定义类型的行为非常不同。

struct A  ;

struct B  B(const A& )  ;

template <class T>
void foo(const T&&) = delete;  // 1 - deleted rvalue overload. const intentional.

void foo(B)                  // 2

void foo(int)                // 3

int main(int argc, char* argv[])

  A a;
  foo(a);   // This resolves to 2
  foo(3.3); // This resolves to 1
  foo(2);   // This resolves to 3 (as expected).
       

为什么删除的右值重载会阻止隐式转换为 int 而不是从一种用户定义类型转换为另一种?

【问题讨论】:

MM 指出我的代码示例混淆了问题。 这句话的后半部分也不准确,“高完整性 C++ 标准建议可以删除函数的右值参数,从而防止隐式转换。”。可以删除 RValue 重载,但 HIPCC 并不建议这样做可以防止隐式转换。 【参考方案1】:

高完整性 C++ 标准建议将右值参数 可以删除函数,从而防止隐式转换。

不,只有 转发引用 重载会为重载集中的所有其他重载禁用 ICS (Implicit Conversion Sequence)。让它成为转发参考,see ICS disabled (Coliru Link)

template <class T>
void foo(const T&&) = delete;  // introduces a qualification match

上面的代码为重载添加了一个限定匹配。因此,ICS 仍在发挥作用。

为什么foo(3.3) 失败是因为3.3double 类型的prvalue,与转换为int 相比,它更适合右值重载。因为资格匹配是ranked better而不是转化匹配

【讨论】:

声明 void foo(const B&amp;&amp;) = delete; 会阻止隐式转换。我的代码示例虽然混淆了问题。我会发布一个后续问题。 @jbcoe,是的,删除特定类型的 const rvalue 重载也会禁用该类型的 ICS。但是删除 转发引用 重载会禁用整个重载集的 ICS。 谢谢。为什么函数和函数模板的行为不同? 删除转发引用过于激进,因为它会阻止使用非常量左值引用进行调用。 发布后续问题:***.com/questions/43995946/…【参考方案2】:

在您的代码中,用户定义类型和原始类型之间的处理方式没有区别。这两行的行为区别:

foo(a);
foo(3.3);

a 是左值,3.3 是右值。右值参数匹配你的重载1(它只接受右值),左值参数不匹配。

如果您尝试使用右值参数调用 foo&lt;A&gt;,它也会匹配 1 并失败,例如foo(A);.

【讨论】:

你是对的。我对此有点困惑。为什么隐式转换也不匹配右值重载? (也许这值得在一个单独的问题中提出?) @jbcoe 在重载解析中,模板根据提供的参数进行实例化。 然后,在实例化和非模板函数之间发生重载决议(这涉及考虑隐式转换序列等)。 例如,在foo(a) 中,重载集是foo(B)foo(int) - 没有可能的模板实例化a 作为参数。但是对于foo(3.3),重载集是foo&lt;double&gt;(const double&amp;&amp;)foo(B)foo(int)。排名过程选择这三个中的第一个,因为直接引用绑定在浮点到整数转换之前排名 发布后续问题:***.com/questions/43995946/…【参考方案3】:

有 3 种可能的重载

1 是可行的。 2 是可行的 3 不是

2 更好的匹配(模板(非精确匹配)与常规方法(使用一个用户定义转换))。

你可以看看http://en.cppreference.com/w/cpp/language/overload_resolution 查看所需的完整规则集

【讨论】:

以上是关于为啥我可以阻止基元而不是用户定义类型的隐式转换?的主要内容,如果未能解决你的问题,请参考以下文章

为啥“as”运算符在 C# 中不使用隐式转换运算符?

7. 类

JavaScript的数据类型的隐式转换

在 .NET 中使用隐式转换替代多重继承

使用接口的隐式运算符

Scala中的隐式转换|理解