【问题标题】:"function template has already been defined" using "std::enable_if_t" [duplicate]“功能模板已被定义”使用“std::enable_if_t”[重复]
【发布时间】:2020-01-30 20:06:35
【问题描述】:

我正在尝试使用std::enable_if_t 根据传递给它的类型的特征来切换模板函数的实现。

这是一个例子:

#include <iostream>
#include <type_traits>

// Enable if "T" is integral
template <typename T,
    typename = std::enable_if_t<std::is_integral_v<T>>
>
void print(T value)
{
    std::cout << "Integral: " << value << std::endl;
}

// Enable if "T" is not integral
template <typename T,
    typename = std::enable_if_t<!std::is_integral_v<T>>
>
void print(T value)
{
    std::cout << "Not Integral: " << value << std::endl;
}

int main()
{
    int i = 42;
    print(i);

    double d = 42.0;
    print(d);
}

问题是,这无法编译说明:

'void print(T)':函数模板已经定义。

这让我感觉很奇怪,因为 std::is_integral_v&lt;T&gt;!std::is_integral_v&lt;T&gt; 永远不会同时评估为 true,所以每当启用一个实现时,另一个实现应该被禁用。

为什么这不起作用?解决此问题并获得我正在寻找的功能的最佳方法是什么?

【问题讨论】:

    标签: c++ templates c++17 template-meta-programming enable-if


    【解决方案1】:

    CPP Reference:

    一个常见的错误是声明两个仅不同的函数模板 在他们的默认模板参数中。这不起作用,因为 声明被视为同一函数的重新声明 模板(默认模板参数不考虑在函数中 模板等价)。

    /*** WRONG ***/
    
    struct T {
        enum { int_t,float_t } m_type;
        template <typename Integer,
                  typename = std::enable_if_t<std::is_integral<Integer>::value>
        >
        T(Integer) : m_type(int_t) {}
    
        template <typename Floating,
                  typename = std::enable_if_t<std::is_floating_point<Floating>::value>
        >
        T(Floating) : m_type(float_t) {} // error: treated as redefinition
    };
    
    /* RIGHT */
    
    struct T {
        enum { int_t,float_t } m_type;
        template <typename Integer,
                  std::enable_if_t<std::is_integral<Integer>::value, int> = 0
        >
        T(Integer) : m_type(int_t) {}
    
        template <typename Floating,
                  std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0
        >
        T(Floating) : m_type(float_t) {} // OK
    };
    

    在模板类型中使用 enable_if 时应小心 命名空间范围函数模板的非类型参数。一些 ABI Itanium ABI 等规范不包括 中的非类型模板参数的实例化相关部分 mangling,意味着两个不同功能的专业化 模板可能以相同的错误名称结束并错误 联系在一起。例如:

    // first translation unit
    
    struct X {
        enum { value1 = true, value2 = true };
    };
    
    template<class T, std::enable_if_t<T::value1, int> = 0>
    void func() {} // #1
    
    template void func<X>(); // #2
    
    // second translation unit
    
    struct X {
        enum { value1 = true, value2 = true };
    };
    
    template<class T, std::enable_if_t<T::value2, int> = 0>
    void func() {} // #3
    
    template void func<X>(); //#4
    

    函数模板#1 和#3 具有不同的签名,并且是 不同的模板。尽管如此,#2和#4,尽管是 不同功能模板的实例化,具有相同的损坏 Itanium C++ ABI (_Z4funcI1XLi0EEvv) 中的名称,这意味着 链接器会错误地认为它们是同一个实体。

    修复:

    #include <iostream>
    #include <type_traits>
    
    // Enable if "T" is integral
    template <typename T,
        std::enable_if_t<std::is_integral_v<T>, int> = 0
    >
    void print(T value)
    {
        std::cout << "Integral: " << value << std::endl;
    }
    
    // Enable if "T" is not integral
    template <typename T,
        std::enable_if_t<!std::is_integral_v<T>, int> = 0
    >
    void print(T value)
    {
        std::cout << "Not Integral: " << value << std::endl;
    }
    
    int main()
    {
        int i = 42;
        print(i);
    
        double d = 42.0;
        print(d);
    }
    

    更好的是,使用概念:

    #include <iostream>
    #include <concepts>
    
    // Enable if "T" is integral
    template <std::integral T>
    void print(T value)
    {
        std::cout << "Integral: " << value << std::endl;
    }
    
    // Enable if "T" is not integral
    template <typename T>
    void print(T value)
    {
        std::cout << "Not Integral: " << value << std::endl;
    }
    
    int main()
    {
        int i = 42;
        print(i);
    
        double d = 42.0;
        print(d);
    }
    

    LIVE

    【讨论】:

    • 谢谢!该链接准确地描述了这个问题!
    • 目前有哪些编译器支持概念?这才是我真正追求的!我认为他们还没有得到广泛的支持。
    • @tjwrona1992 GCC 支持概念已经有一段时间了。 GCC 10 支持 &lt;concepts&gt; 标头。请参阅link
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 2017-02-14
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    相关资源
    最近更新 更多