在Clang中使用enable_if进行模板专业化失败,可在GCC中使用

时间:2019-02-11 15:26:04

标签: c++ templates c++14 c++17

我正在尝试根据模板类型删除成员函数。问题是在以后的模板特化不删除的情况下,使其与我函数的类型签名匹配。

我尝试了以下代码,该代码可使用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.

我想知道如何使代码正常工作,以及为什么所有主要编译器的结果不一致。

3 个答案:

答案 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<>