I've not been able to find a better title, but feel free to modify it if you have the right idea. As it is, it's better than GCC vs clang anyway.
I'm trying to figure out what's wrong in this code:
template <typename... T>
struct S;
template <typename T, typename... U>
struct S<T, U...>: S<U...> {
using S<U...>::f;
template<void(*F)(const T &)>
void f() { }
};
template<>
struct S<> {
void f();
};
template<typename... T>
struct R: S<T...> {
using S<T...>::f;
template<typename U, void(*F)(const U &)>
void g() {
this->template f<F>();
}
};
void h(const double &) { }
int main() {
R<int, double> r;
r.g<double, h>();
}
It compiles with GCC 4.9 (see here), but it doesn't compile with clang 3.8.0 (see here).
Which compiler is right and why?
Moreover, what could I do to see the code compiling with both the compilers?
答案 0 :(得分:9)
我相信clang在这里是正确的,这是一个gcc bug。首先,让我们从一个简单的例子开始:
struct U {
template<int > void foo() { }
};
struct X : U {
using U::foo;
template<void* > void foo() { }
};
int main() {
X{}.foo<1>(); // gcc ok, clang error
}
当 using-declaration 将基类的声明带入派生类时,成员函数和 派生类中的成员函数模板覆盖和/或隐藏成员函数和成员函数 具有相同名称的模板,参数类型列表(8.3.5),cv-qualification和ref-qualifier(如果有) 基类(而不是冲突)。这些隐藏或覆盖的声明被排除在集合之外 using-declaration 引入的声明。
U::foo
和X::foo
具有相同的名称,参数类型列表(无†),cv-qualification(无)和ref限定符(无)。因此,X::foo
隐藏 U::foo
而不是重载它,我们肯定会将错误的类型传递到X::foo
。
简单地重载不同的函数指针类型(而不是将它们作为模板参数)可以正常工作:
template <typename T, typename... U>
struct S<T, U...> : S<U...> {
using S<U...>::f;
void f(void (*F)(const T&)) { }
};
或者,如果您确实需要函数指针作为模板参数,仍然可以通过将它们包装在标记类型中来重载:
template <class T>
using fptr = void(*)(const T&);
template <class T, fptr<T> F>
using func_constant = std::integral_constant<fptr<T>, F>;
并通过以下方式传播:
template <typename T, typename... U>
struct S<T, U...>: S<U...> {
using S<U...>::f;
template <fptr<T> F>
void f(func_constant<T, F> ) { }
};
和
template <class U, fptr<U> F>
void g() {
this->f(func_constant<U, F>{});
}
† parameter-type-list的定义并未提及模板参数。
答案 1 :(得分:3)
@barry之后 - 将f
专精中S
的定义更改为:
template <typename T, typename... U>
struct S<T, U...>: S<U...> {
using S<U...>::f;
template<void(*F)(const T &)>
void f(T *= nullptr) { }
};
还可以使用您的代码。
修改强>
为了使代码更简单,您可以将专业化更改为:
template <typename T, typename... U>
struct S<T, U...>: S<T>, S<U...> {
};
template<typename T>
struct S<T> {
template<void(*F)(const T &)>
void f() { }
}
然后使用:
在f
中调用g
方法
S<U>::template f<F>();
使用此选项,您可以更进一步,因为@barry建议使用标签调度但在模板参数中而不是作为函数的参数:
template <typename... T>
struct S;
template <typename T>
struct footag { };
template <typename T, typename... U>
struct S<T, U...>: S<footag<T>>, S<U...> {
};
template<typename T>
struct S<T>:S<footag<T>> {
};
template<typename T>
struct S<footag<T>> {
template <void (*F)(const T&)>
void f() { }
};
template<typename V, typename... T>
struct R: S<V, T...> {
template<typename U, void(*F)(const U &)>
void g() {
S<footag<U>>::template f<F>();
}
};
void h(const float &) { }
int main() {
R<int, float, double> r;
r.g<float, h>();
}