我正在尝试根据模板类型删除成员函数。问题是在以后的模板特化不删除的情况下,使其与我函数的类型签名匹配。
我尝试了以下代码,该代码可使用GCC(9.0.1)进行编译,但是在Clang(9.0.0)中给出了错误。我认为它也无法在MSVC ++中构建代码。
#include <type_traits>
#include <iostream>
template <typename T>
struct my_type {
template <typename Q = T>
std::enable_if_t<!std::is_same<bool, Q>::value, my_type<T>> my_fun(const my_type<T>& v) {
std::cout << "Base";
return v;
}
};
template <>
template <typename Q>
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>> my_type<double>::my_fun(const my_type<double>& v) {
std::cout << "Specialized";
return v;
}
int main()
{
my_type<double> aa, bb;
aa.my_fun(bb);
}
Clang的错误是
prog.cc:16:88: error: out-of-line definition of 'my_fun' does not match any declaration in 'my_type<double>'
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>> my_type<double>::my_fun(const my_type<double>& v) {
^~~~~~
1 error generated.
我想知道如何使代码正常工作,以及为什么所有主要编译器的结果不一致。
答案 0 :(得分:3)
在两种情况下:my_type
专用于double
。然后比较my_fun
template < >
template <typename Q>
std::enable_if_t<!std::is_same_v<bool, Q>::value, my_type<double>>
// ^ (!)
my_type<double>::my_fun(const my_type<double>& v)
针对完全专门的my_fun
:
template < >
template < >
// ^
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>>
my_type<double>::my_fun<double>(const my_type<double>& v)
// ^
以上两种变体都是合法的;相比之下,您最终介于两者之间...
GCC接受此代码对我来说不合适,我在注释中加入了“这是一个错误”部分。
也许更糟:考虑my_type<double>::my_fun<bool>
专业化–它应该仍然存在,不是吗?
答案 1 :(得分:2)
我不知道如何通过专业化来完成这项工作。但是我确实知道如何完全回避问题:
template <typename> struct tag { };
template <typename Q = T>
std::enable_if_t<!std::is_same_v<bool, Q>, my_type<T>> my_fun(const my_type<T>& v) {
return my_fun_impl(v, tag<Q>{});
}
具有:
template <typename U>
my_type my_fun_impl(const my_type& v, tag<U>) {
std::cout << "Base";
return v;
}
my_type my_fun_impl(const my_type& v, tag<double>) {
std::cout << "Specialized";
return v;
}
如果您想让专业化为用户提供添加专业化实现的能力,则可以使my_fun_impl
为自由函数,而不是成员函数。如果目标只是专门针对某些类型,则可以使它们成为私有成员函数。
答案 2 :(得分:1)
您不能在此处使用enable_if
来抑制成员函数,具体取决于类的模板参数,即T
(但仅取决于函数的模板参数) ,即Q
。
您的代码是错误的,正如clang正确指出的那样。我不知道为什么gcc会接受它,以及它如何检测到Q
在您的“专业化”中(我认为您用gcc编译的代码表示为“ Base” –是吗?而且因为没有继承,所以不清楚为什么使用"Base"
。)
不带标签类型,您可以执行以下操作。
template <typename T>
struct my_type {
private:
template<bool Standard>
std::enable_if_t<Base, my_type> my_fun_impl(const my_type& v)
{
std::cout << "Standard";
return v;
}
template<bool Standard>
std::enable_if_t<!Standard, my_type> my_fun_impl(const my_type& v)
{
std::cout << "Specialised";
return v;
}
public:
my_type my_fun(const my_type& v)
{
return my_fun_impl<is_standard<T>::value>(v);
}
};
根据需要选择is_standard<>
。