令我惊讶的是,clang 无法编译以下 c++11 代码:
#include <type_traits>
template<class T>
struct A {};
template<class T>
struct B : A<T> { T x; };
template<class T>
const typename std::remove_reference<T>::type& add_const(T&& x) { return x; }
template<class T>
T& cast_away_const(const T& x) { return const_cast<T&>(x); }
template<class T>
const B<T>& downcast(const A<T>& x) { return static_cast<const B<T>&>(x); }
template<class T>
auto downcast(A<T>& a) ->
decltype(cast_away_const(downcast(add_const(a)))) {
return cast_away_const(downcast(add_const(a)));
}
int main() {
B<int> b;
b.x = 42;
A<int>& a = b;
B<int>& b_again = downcast(a);
return b_again.x;
}
更准确地说,clang 3.7.1 抱怨模板实例化链很长,表现得好像auto downcast(A<T>& a) -> /*...*/
withT=int
的实例化需要自身的实例化。clang 5.0.0 或 6.0.0 只会崩溃(!)。gcc 和 msvc 都接受。
现在,clang-5.0.0 崩溃显然是一个错误,但 clang-3.7.1 错误是有道理的,所以…… 该代码首先是合法的吗?
让我强调一下,我问的是这段代码是否合法,而不是解决方法(一个简单的解决方法是拆分overload
and overload
,overload_const
让后者只处理 const 引用,让前者调用后者)。
我可以在clang和gcc之间找到以下一些相关的争论点(使用带有可变参数模板函数的decltype的尾随返回类型,SO链接)以及这个类似的问题,其答案引用了标准(gcc可以编译可变参数模板而clang不能)
但是,我不确定此处是否适用完全相同的参数(因为downcast
应该引用先前声明的重载,而不是尾随返回类型中的本身)。