3

任何人都知道为什么第一个程序编译但第二个程序没有?唯一的区别是第一个使用普通函数,而第二个使用模板函数。为什么重载解析在模板和非模板函数的位域上表现不同?

回答时请参考标准中的段落。谢谢。

a.cpp

struct X {
  int x : 20;
  int y : 12;
};

void f(const int& x) {}

void f(int&& x) {}

int main() {
  X x;
  f(x.x);
}

b.cpp

struct X {
  int x : 20;
  int y : 12;
};

template <typename T>
void f(T&& x) {}

template <typename T>
void f(const T& x) {}

int main() {
  X x;
  f(x.x);
}

编译器错误:

[hidden]$ g++ -v 2>&1 | tail -n 1
gcc version 4.7.2 20120921 (Red Hat 4.7.2-2) (GCC)
[hidden]$ clang++ -v 2>&1 | head -n 1
clang version 3.3
[hidden]$ g++ -std=c++11 a.cpp
[hidden]$ g++ -std=c++11 b.cpp
b.cpp: In function ‘int main()’:
b.cpp:14:8: error: cannot bind bitfield ‘x.X::x’ to ‘int&’
[hidden]$ clang++ -std=c++11 a.cpp
[hidden]$ clang++ -std=c++11 b.cpp
b.cpp:14:5: error: non-const reference cannot bind to bit-field 'x'
  f(x.x);
    ^~~
b.cpp:2:7: note: bit-field is declared here
  int x : 20;
      ^
1 error generated.
4

1 回答 1

4

错误很明显,您不能对bitfields进行非常量引用[class.bit]/3

地址运算符 & 不应应用于位域,因此没有指向位域的指针。非常量引用不应绑定到位字段 (8.5.3)。[ 注意:如果 const T& 类型的引用的初始化器是一个引用位域的左值,则该引用绑定到一个临时初始化以保存位域的值;引用不直接绑定到位域。见 8.5.3。——尾注]

重载决议行为不同的原因与通用引用有关。参考折叠规则和模板可以做到这一点:

template <typename T>
void f(T&& x) {}

结果T&&被推断为int&当应用于非常量 lvalue int时,情况就是x.x. 在这种特殊情况下,您将得到:

void f(int& x){}
void f(int const& x){}

而第一个,从参考折叠规则得到的f(T&& x),可以明显看出比后一个匹配的更好。

于 2012-12-24T22:42:25.430 回答