如何使用std::is_integral<>来选择实现?

16

如果 std::is_integral<>::value 为真,我想返回一个 int64_t

否则,我想在该对象上调用 to_int64t()

我的尝试失败了,因为函数模板的部分特化是不允许的。

代码

#include <type_traits>
#include <cstdint>

template<class T,bool is_integral_type>
int64_t to_int64t( const T& t )
{
        return t;
}

template<class T>
int64_t to_int64t<T,std::is_integral<T>::value>( const T& t )
{
        return t;
}

template<class T>
int64_t to_int64t<T,!std::is_integral<T>::value>( const T& t )
{
        return t.to_int64t();
}

int main()
{
        int64_t i = 64;
        auto x = to_int64t( i );
}

可能重复:https://dev59.com/MWct5IYBdhLWcg3wedOP - legends2k
2个回答

33

函数模板不能进行部分特化,通常情况下,使用函数模板特化并不是一个好主意。

实现您想要的功能的一种方法是使用称为“标签派遣”的技术,这基本上包括提供一个转发函数,该函数根据额外虚拟参数的值选择正确的重载:

#include <type_traits>
#include <cstdint>

template<class T>
int64_t to_int64t( const T& t, std::true_type )
{
    return t;
}

template<class T>
int64_t to_int64t( const T& t, std::false_type )
{
    return t.to_int64t();
}

template<class T>
int64_t to_int64t( const T& t )
{
    return to_int64t(t, std::is_integral<T>());
}

int main()
{
    int64_t i = 64;
    auto x = to_int64t( i );
}

另一种可能性是使用基于 std::enable_if 的经典 SFINAE 技术。以下是可能的实现方式(请注意,自 C++11 起,函数模板上允许使用默认模板参数):
#include <type_traits>
#include <cstdint>

template<class T, typename std::enable_if<
    std::is_integral<T>::value>::type* = nullptr>
int64_t to_int64t( const T& t )
{
    return t;
}

template<class T, typename std::enable_if<
    !std::is_integral<T>::value>::type* = nullptr>
int64_t to_int64t( const T& t )
{
    return t.to_int64t();
}

int main()
{
    int64_t i = 64;
    auto x = to_int64t( i );
}

另一种可能性,虽然更冗长,是在detail命名空间中定义辅助类模板(可以部分特化),并提供全局转发器 - 尽管我不会在此用例中使用此技术,但我正在展示它,因为它可能在相关的设计情况下很有用:

#include <type_traits>
#include <cstdint>

namespace detail
{
    template<class T, bool = std::is_integral<T>::value>
    struct helper { };

    template<class T>
    struct helper<T, true>
    {
        static int64_t to_int64t( const T& t )
        {
            return t;
        }
    };

    template<class T>
    struct helper<T, false>
    {
        static int64_t to_int64t( const T& t )
        {
            return t.to_int64t();
        }
    };
}

template<class T>
int64_t to_int64t( const T& t )
{
    return detail::helper<T>::to_int64t(t);
}

int main()
{
    int64_t i = 64;
    auto x = to_int64t( i );
}

哦,这太美了。 :) - David G
3
@Alon:函数模板上的默认模板参数是在C++11中引入的,因此您应该使用std=c++11选项进行编译(如果不支持,则可能需要升级编译器到最新版本)。 - Andy Prowl
@AndyProwl:你能解释一下编译器吗?如果 enable_if 失败了,它只是不选择这个函数吗? - Alon
1
@Alon:是的,这就是所谓的SFINAE(Substitution Failure Is Not An Error):编译器尝试实例化函数模板的签名,如果失败,它只会丢弃相应的函数重载集合,并仅考虑其他可行的函数。 - Andy Prowl
@AndyProwl:1和3都是标签分派。其中1是按实例进行标签分派,而3是按类型进行标签分派。~来源 - legends2k
显示剩余7条评论

8
您可以直接使用std::enable_if
template<class T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
int64_t to_int64t( const T& t )
{
        return t;
}

template<class T, typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
int64_t to_int64t( const T& t )
{
        return t.to_int64t();
}

我认为这是比被接受的更好的实现方式?它不起作用吗? - John
同意。是的,它在Windows上与VC++和在MacOS上与Clang都非常好用。 - J Evans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接