假设以下代码:
#include <iostream>
template<typename T>
struct Link
{
Link(T&& val) : val(std::forward<T>(val)) {}
T val;
};
template<typename T>
std::ostream& operator<<(std::ostream& out, const Link<T>& link)
{
out << "Link(" << link.val << ")";
return out;
}
template<typename T>
auto MakeLink(T&& val) -> Link<T>
{
return {std::forward<T>(val)};
}
namespace Utils {
template<typename Any>
constexpr auto RemoveLinks(const Any& any) -> const Any&
{
return any;
}
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
{
return RemoveLinks(link.val);
}
} /* Utils */
int main()
{
int k = 10;
auto link = MakeLink(MakeLink(k));
std::cout << link << std::endl;
std::cout << Utils::RemoveLinks(link) << std::endl;
}
由于某种原因我无法理解,它会生成以下编译错误g++-4.8
:
/home/allan/Codes/expr.cpp: In instantiation of ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const int&]’:
/home/allan/Codes/expr.cpp:88:32: required from ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = Link<int&>; decltype (Utils::RemoveLinks(link.val)) = const int&]’
/home/allan/Codes/expr.cpp:100:41: required from here
/home/allan/Codes/expr.cpp:88:32: error: invalid initialization of reference of type ‘const Link<int&>&’ from expression of type ‘const int’
return RemoveLinks(link.val);
^
/home/allan/Codes/expr.cpp:89:1: error: body of constexpr function ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const Link<int&>&]’ not a return-statement
}
^
/home/allan/Codes/expr.cpp: In function ‘constexpr decltype (Utils::RemoveLinks(link.val)) Utils::RemoveLinks(const Link<T>&) [with T = int&; decltype (Utils::RemoveLinks(link.val)) = const int&]’:
/home/allan/Codes/expr.cpp:89:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
而 clang 3.3 给出:
test.cc:34:12: error: reference to type 'const Link<int &>' could not bind to an lvalue of type 'const int'
return RemoveLinks(link.val);
^~~~~~~~~~~~~~~~~~~~~
test.cc:46:25: note: in instantiation of function template specialization 'Utils::RemoveLinks<Link<int &> >' requested here
std::cout << Utils::RemoveLinks(link) << std::endl;
但是,如果命名空间Utils
被删除,然后它编译没有错误(gcc 和 clang),并且执行输出:
Link(Link(10))
10
为什么要定义这些模板函数(RemoveLinks
)在命名空间中会导致此类错误?
此问题是声明点 (1) 与依赖名称查找 (2) 相结合的问题的结果。
(1)在声明中
template<typename T>
constexpr auto RemoveLinks(const Link<T>& link) -> decltype(RemoveLinks(link.val))
名字RemoveLinks
,或者更准确地说,这种超载RemoveLinks
,仅可见在完整的声明符之后根据 [basic.scope.pdecl]/1。这尾随返回类型是声明符的一部分,按照 [dcl.decl]/4。另请参阅这个答案.
(2)在表达式中RemoveLinks(link.val)
, 名字RemoveLinks
取决于 [temp.dep]/1,如link.val
是依赖的。
如果我们现在查找依赖名称是如何解析的,我们会发现 [temp.dep.res]:
在解析从属名称时,会考虑以下来源的名称:
- 在模板定义时可见的声明。
- 来自与来自实例化上下文和定义上下文的函数参数类型相关联的命名空间的声明。
第一个项目符号没有找到第二个过载RemoveLinks
由于声明点 (1)。第二个没有找到重载,因为名称空间Util
不与任何参数相关联。这就是为什么将所有内容都放在全局命名空间或命名空间中Util
按预期工作(实例).
出于同样的原因,使用合格的 ID in the 尾随返回类型 (like -> decltype(Util::RemoveLinks(link.val))
在这里没有帮助。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)