重载解析和数组:应该调用哪个函数? [英] Overload resolution and arrays: which function should be called?

查看:46
本文介绍了重载解析和数组:应该调用哪个函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下程序:

#include <cstddef>
#include <cstdio>

void f(char const*&&)      { std::puts("char const*&&");      } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)

template <std::size_t N>
void f(char const (&)[N])  { std::puts("char const(&)[N]");   } // (3)

int main()
{
    const char data[] = "a";
    f(data);
}

应该调用哪个 f?为什么?

Which f should be called? Why?

三个编译器的最新发布版本在这个问题的答案上存在分歧:

The latest released versions of three compilers disagree on the answer to this question:

  • (1) 在使用 g++ 4.5.2
  • 编译程序时调用
  • (2) 在使用 Visual C++ 2010 SP1 编译程序时调用
  • (3) 在使用 Clang 3.0 (trunk 127530) 编译程序时调用
  • (1) is called when the program is compiled using g++ 4.5.2
  • (2) is called when the program is compiled using Visual C++ 2010 SP1
  • (3) is called when the program is compiled using Clang 3.0 (trunk 127530)

重载解析规则在不同的 C++0x 草案中是否发生了实质性的变化?或者,其中两个编译器真的完全错误吗?哪个重载是根据最新的 C++0x 草案选择的正确重载?

Have the overload resolution rules changed substantially in different C++0x drafts? Or, are two of these compilers really just completely wrong? Which overload is the correct overload to be selected per the latest C++0x draft?

推荐答案

首先,三个的转换顺序是一样的,只是前两个有一个左值转换(lvalue to rvalue conversion),但是不用于排序转换序列.这三个都是完全匹配的(函数模板特化具有参数类型char const(&)[2]).

First, the conversion sequence of all three is the same, except that for the first two, there is an lvalue transformation (lvalue to rvalue conversion), which however is not used in ordering conversion sequences. All three are exact matches (the function template specialization has parameter type char const(&)[2]).

如果你在 13.3.3.2p3 处迭代规则,你会停在这一段

If you iterate over the rules at 13.3.3.2p3, you stop at this paragraph

S1 和 S2 是引用绑定 (8.5.3) 并且都不是引用没有引用限定符声明的非静态成员函数的隐式对象参数,S1 将右值引用绑定到右值,S2 绑定左值参考.

S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

如果需要将右值引用绑定到左值,则无法形成转换序列,规范在 13.3.3.1.4p3 中说.如果您查看参考绑定在 8.5.3p5 最后一个项目中的工作原理,它将从数组中创建一个类型为 char const* 的临时(我认为它们的意思是 rvalue 临时)左值并将引用绑定到该临时对象.因此,我认为(1)(2) 更好.(1)(3) 的情况相同,尽管我们不需要它,因为 (3) 是一个模板,所以并列,我们会再次选择(1).

A conversion sequence cannot be formed if it requires binding an rvalue reference to an lvalue, the spec says at 13.3.3.1.4p3. If you look at how reference binding works at 8.5.3p5 last bullet, it will create a temporary (I think they meant rvalue temporary) of type char const* from the array lvalue and bind the reference to that temporary. Therefor, I think (1) is better than (2). Same holds for (1) against (3), although we wouldn't need this because (3) is a template so in a tie, we would choose (1) again.

n3225中,他们改变了引用绑定规则,使得右值引用可以绑定到作为左值的初始化表达式,只要引用将绑定到一个右值(可能是通过转换初始化器创建的)之前正确).这可能会影响 Visual C++ 的处理,这里可能不是最新的.

In n3225, they changed the reference binding rules so that rvalue references can bind to initializer expressions that are lvalues, as long as the reference will be bound to an rvalue (possibly created by converting the initializer properly before). This could influence the handling by Visual C++, which may not be up to date here.

我不确定叮当声.即使它会忽略 (1),那么它最终会在 (2)(3) 之间并列,并且需要选择 (2) 因为它是一个非模板.

I'm not sure about clang. Even if it would ignore (1), then it would end up in a tie between (2) and (3), and would need to choose (2) because it's a non-template.

我认为 8.5.3p5 的最后一个项目符号令人困惑,因为它说否则类型为临时的..".目前尚不清楚 13.3.3.1.4p3 将临时值视为左值还是右值,这意味着我不确定根据规范的确切字词,以下内容应该如何真正表现

I think that 8.5.3p5 last bullet is confusing because it says "Otherwise a temporary of type ..". It's not clear whether the temporary is regarded as an lvalue or as an rvalue by 13.3.3.1.4p3, which means I'm not sure how the following should really behave according to the exact words of the spec

void f(int &);
void f(int &&);

int main() {
  int n = 0;
  f(n);
}

如果我们假设临时变量被第 13 条视为右值,那么我们将一个右值引用绑定到第二个函数中的一个右值,并在第一个函数中绑定一个左值.因此,我们将选择第二个函数,然后通过 8.5.3p5 最后一个项目符号获得诊断,因为 T1T2 是与引用相关的.如果我们假设临时文件被第 13 条视为左值,那么以下内容将不起作用

If we assume the temporary is treated as an rvalue by clause 13, then we bind an rvalue ref to an rvalue in the second function and an lvalue in the first. Therefor, we will choose the second function and then get a diagnostic by 8.5.3p5 last bullet because T1 and T2 are reference-related. If we assume the temporary is treated as an lvalue by clause 13, then the following would not work

void f(int &&);
int main() {
  f(0);
}

因为我们会将一个右值引用绑定到一个左值,根据第 13 条,这将使该函数不可行.如果我们将将右值引用绑定到左值"解释为引用初始化表达式而不是绑定到的最终表达式,我们将不会接受以下内容

Because we would bind an rvalue ref to an lvalue which by clause 13 will make the function non-viable. And if we interpret "binding an rvalue ref to an lvalue" to refer to the initializer expression instead of the final expression bound to, we won't accept the following

void f(float &&);
int main() {
  int n = 0;
  f(n);
}

然而,这从 n3225 开始是有效的.所以似乎有些混乱——我就此向委员会发送了一份 DR.

This however is valid as of n3225. So there seems to be some confusion - I sent a DR to the committee about this.

这篇关于重载解析和数组:应该调用哪个函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆