模板SFINAE和enable_if在动态和固定大小之间切换

时间:2015-10-04 19:45:49

标签: c++ templates sfinae

我是SFINAE的新手。我有一个模板,我希望能够接受可以简单地调用sizeof(x)来确定大小的类,或者如果值是动态的,则需要x.size()。

我试图围绕这样看起来如何平滑,我认为界面:size_t size(const Item& item)似乎已经足够好了。

以下是一个有效的示例:

#include <iostream>
#include <cstdio>

#include <type_traits>

template <typename T>
class Fixed {
public:
    typedef T Item;

    static const bool kFixedSize = true;

    static size_t size() {
        return sizeof(T);
    }
};

template <typename T>
class Dynamic {
public:
    typedef T Item;

    static const bool kFixedSize = false;

    static size_t size(const T& item) {
        return item.size();
    }
};

template <typename T>
class Serialize {
public:
    template <typename = typename std::enable_if<T::kFixedSize> >
    size_t size(typename T::Item&) {
        return T::size();
    }

    template <typename = typename std::enable_if<!T::kFixedSize> >
    size_t size(const typename T::Item& item) {
        return T::size(item);
    }
};


int main() {
    Serialize< Fixed<int> > fixed;
    int a = 0;
    std::cout << fixed.size(a) << std::endl;

    Serialize< Dynamic<std::string> > dynamic;
    std::cout << dynamic.size("string") << std::endl;

    return 0;
}

它有一个问题,虽然一个是:size_t size(typename T::Item&)而另一个是size_t size(const typename T::Item& item),否则我正在重载模板的编译器合规性。第二个看起来似乎太匹配非常棘手的代码来实现目标 - 有更好的方法来做到这一点吗?

3 个答案:

答案 0 :(得分:0)

我相信你想要这样的东西

//class hierarchy to set the priority for type matching
struct second_priority
{
};
struct first_priority : public second_priority
{};

template<typename T>
auto size_impl(T const & data, second_priority t) -> int
{
    return sizeof(data);
}

template<typename T>
auto size_impl(T const & data , first_priority t) -> decltype(data.size(),int())
{
   return data.size();
}

template<typename T>
int size(T const & data )
{
    return size_impl(data,first_priority{});    
}

答案 1 :(得分:0)

我认为@Gautam Jha使用SFINAE提出了一个很好的解决方案。你可以通过在'else'的情况下使用省略号来缩短它,所以你不需要使用这个辅助类,它的继承:

template<typename T>
auto size_impl(T const & item, int) -> decltype(item.size())
{
  return item.size();
}


template<typename T>
auto size_impl(T const & item, ...) -> size_t
{
  return sizeof(T);
}

template<typename T>
auto size(T const & item) -> size_t
{
  return size_impl(item, 0);
}

你玩SFINAE很酷,但通常有更简单(即阅读和理解)的方法来实现相同的目标,请参阅POW的解决方案(不幸的是已被删除)。

答案 2 :(得分:0)

由于你要做的就是调用不同的函数来获得DynamicFixed的大小,你可以用不同的方式实现这些类,并在Serialize中使用它们:

#include <iostream>
#include <cstdio>

#include <type_traits>

template <typename T>
class Fixed {
public:
    typedef T Item;

    static size_t size(const T&) {
        return sizeof(T);
    }
};

template <typename T>
class Dynamic {
public:
    typedef T Item;

    static size_t size(const T& item) {
        return item.size();
    }
};

template <typename T>
class Serialize {
public:
   size_t size(typename T::Item const& x) {
        return T::size(x);
    }
};


int main() {
    Serialize< Fixed<int> > fixed;
    int a = 0;
    std::cout << fixed.size(a) << std::endl;

    Serialize< Dynamic<std::string> > dynamic;
    std::cout << dynamic.size( std::string{"string"} ) << std::endl;

    return 0;
}

但是,我会考虑使用类型特征或自由函数来做同样的事情。这将是更具可扩展性的,因为您必须为新类型提供新特征或重载,例如一些只有长度法的容器。

#include <iostream>
#include <cstdio>

#include <type_traits>

size_t size(int) {return sizeof(int);}
size_t size(std::string const& s) {return s.size();}

template<typename T>
struct size_trait
{

};

template<>
struct size_trait<int>
{
    static size_t size(int) {return sizeof(int);}
};

template<>
struct size_trait<std::string>
{
    static size_t size(std::string const& x) {return x.size();}
};

template <typename T>
class Serialize {
public:
   size_t size(T const& x) {
        return ::size(x);
    }

    size_t size_(T const& x) {
        return size_trait<T>::size(x);
    }
};


int main() {
    Serialize< int > fixed;
    int a = 0;
    std::cout << fixed.size(a) << std::endl;
    std::cout << fixed.size_(a) << std::endl;

    Serialize< std::string > dynamic;
    std::cout << dynamic.size( std::string{"string"} ) << std::endl;
    std::cout << dynamic.size_( std::string{"string"} ) << std::endl;

    return 0;
}