如何通过C ++标准类型特征切换传递值?

时间:2016-12-29 03:51:34

标签: c++ c++11 templates typetraits

我想通过C ++标准类型特征切换传递的值。这是测试代码显示我的意思:

template<typename T>
T _func(T t, std::is_integral<T>||std::is_enum<T>)
{
    return t;
}

template<typename T>
T _func(T t, std::is_floating_point<T>)
{
    return t;
}

template<typename T>
T _func(T t, std::is_same<T, std::string>)
{
    return t;
}

template<typename T>
T func(T t)
{
    return _func(t, T)
}

当然没有用。我尽我所能测试并找到实现这个的方法:

int testint(int t){ return t;}
std::string testString(std::string t ){return t;}
float testreal(float t ){return t;}

template <typename T>
T test_string_type(T t, std::true_type) {
    return testString(t);
}

template <typename T>
T test_string_type(T t, std::false_type) {
    return t;
}

template<typename T>
T test_real_type(T t, std::true_type)
{
    return testreal(t) ;
}

template<typename T>
T test_real_type(T t, std::false_type)
{
    return test_string_type(t, std::is_same<T, std::string>());
}

template<typename T>
T test_enum_type(T t, std::true_type)
{
    return testint(t) ;
}

template<typename T>
T test_enum_type(T t, std::false_type)
{
    return test_real_type(t, std::is_floating_point<T>());
}

template<typename T>
T test_integer_type(T t, std::true_type)
{
    return testint(t) ;
}

template<typename T>
T test_integer_type(T t, std::false_type)
{
    return test_enum_type(t, std::is_enum<T>()) ;
}

template<typename T>
T test(T t)
{
    return test_integer_type(t, std::is_integral<T>());
}

它有效,但代码真的很难看。 有什么聪明的方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:3)

您可以使用SFINAE拒绝重载

template<typename T>
typename std::enable_if<std::is_integral<T>::value || std::is_enum<T>::value>::type
 func(T t)
{
    return t;
}

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type
 func(T t)
{
    return t;
}

template<typename T>
typename std::enable_if<std::is_same<T, std::string>::value>::type 
 func(T t)
{
    return t;
}

或使用类似于您所显示的标签调度,但以不同方式处理替代方案以减少案例数量

namespace detail
{
template<typename T>
T func(T t, std::false_type /*is_string*/, std::false_type /*is_float*/, std::true_type /*is_int*/)
{
    return t;
}

template<typename T>
T func(T t, std::false_type /*is_string*/, std::true_type /*is_float*/, std::false_type /*is_int*/)
{
    return t;
}

template<typename T>
T func(T t, std::true_type /*is_string*/, std::false_type /*is_float*/, std::false_type /*is_int*/)
{
    return t;
}
}

template<typename T>
T func(T t)
{
    return detail::func(t, std::is_same<string, T>(),
                           std::is_floating_point<T>(),
                           std::integral_constant<bool, std::is_integral<T>::value || std::is_enum<T>::value>());
}

答案 1 :(得分:3)

仅供记录,在C ++ 17中,您可以使用if constexpr

template <typename T>
T func(T t) {
    if constexpr (std::is_integral<T>::value || std::is_enum<T>::value) {
        return t + 1;
    } else if constexpr (std::is_floating_point<T>::value) {
        return t + 2.5;
    } else if constexpr (std::is_same<T, std::string>::value) {
        return t + " suffix";
    }

    return t;
}

此代码在Clang 3.9和GCC 7中编译。

答案 2 :(得分:1)

您可以通过SFINAE实现这一目标

#include <iostream>
#include <type_traits>
#include <string>

using namespace std;

struct One {};
struct Two {};
struct Three {};

template <typename T,
          typename std::enable_if_t<std::is_integral<T>::value
                || std::is_enum<T>::value>* = nullptr>
One _func(T)
{
    return One{};
}

template<typename T,
         typename std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
Two _func(T)
{
    return Two{};
}

template<typename T,
         typename std::enable_if_t<std::is_same<
            std::decay_t<T>, std::string>::value>* = nullptr>
Three _func(T)
{
    return Three{};
}

template<typename T>
auto func(T t)
{
    return _func(t);
}

template <typename...>
struct WhichType;

int main() {
    WhichType<decltype(func(1))>{};
    WhichType<decltype(func(1.0))>{};
    WhichType<decltype(func(std::string{}))>{};
    return 0;
}

这里有几点需要了解,第一个是SFINAE以及它是如何工作的,但回答这个问题会让答案太长,而且我可能会比其他人做得更糟糕,所以这里有一个{ {1}}(http://en.cppreference.com/w/cpp/types/enable_if)这里是对SFINAE的引用(http://eli.thegreenplace.net/2014/sfinae-and-enable_if/

此处需要注意的另一个重要事项是其中一个功能模板中的std::enable_if。如果传入的类型是std::decay_t,则代码将不再正确,因为const string&为false。 std::is_same<const string&, std::string>::value删除所有引用,const限定符和volatile限定符。见http://en.cppreference.com/w/cpp/types/decay

std::decay_t模板是一种用于检查变量类型的技术,如Scott Meyers在Effective Modern C ++一书中所述。大多数编译器将输出一个错误,显示模板中事物的类型。因此,当您想知道表达式的类型时,它很有用。在这种情况下,它将输出函数的返回类型,允许您查看调用的函数。