C++ 用SFINAE计算不同单元的尺寸 介绍

C++ 用SFINAE计算不同单元的尺寸 介绍,c++,templates,metaprogramming,sfinae,C++,Templates,Metaprogramming,Sfinae,我刚刚开始阅读和研究SFINAE。为了提高我的理解力,我开始自己尝试 所以我一直在想一个有用但简单的方法来使用SFINAE强大的技巧,最后我想到了一组函数来计算一个给定类型占用了多少字节;只要我们处理简单类型,解决方案就很简单: template <typename T> size_t SizeOf(const T &t) { return sizeof(T); }; 是的,在这一点上,似乎根本不需要SFINAE,但是,让我们想想容器。容器的SizeOf必须是Siz

我刚刚开始阅读和研究SFINAE。为了提高我的理解力,我开始自己尝试

所以我一直在想一个有用但简单的方法来使用SFINAE强大的技巧,最后我想到了一组函数来计算一个给定类型占用了多少字节;只要我们处理简单类型,解决方案就很简单:

template <typename T> size_t SizeOf(const T &t)
{
    return sizeof(T);
};
是的,在这一点上,似乎根本不需要SFINAE,但是,让我们想想容器。容器的
SizeOf
必须是
SizeOf(容器类型)
加上每个元素的大小之和,SFINAE在此输入:

template <typename T> size_t SizeOf(const T &t)
{
    size_t Result = sizeof(t);

    for (T::const_iterator i = t.begin(); i != t.end(); ++i)
    {
        Result += SizeOf(*i);
    }

    return Result;
};
上述代码生成此输出:

1: 4
5: 45
6: 58
7: 66
8: 20
  • 如果我取消注释带有2、3和4输出的行,编译器将显示“不明确调用”错误。我真的以为输出2将使用
    is_指针
    specialization,而输出3和4将使用
    is_数组
    1。嗯,我错了,但我不知道为什么

  • 我对获取容器总大小的方式不太满意,我认为在
    std::basic_string
    do
    SizeOf(container)+SizeOf(container::value\u type)*container.size()中,迭代所有项目并为每个项目调用
    SizeOf
    是一个不错的选择,但并非所有容器都如此
    会更快,但我无法了解如何专门处理
    基本字符串

  • 谈到检测类(比如检测iterable和pair的类),在一些关于SFINAE的示例和web示例中,我看到创建
    true\u类型
    false\u类型
    typedef
    s是一种常见的做法,通常定义为
    char
    char[2]
    ;但我发现一些作者使用
    char
    long
    作为
    true\u类型
    false\u类型
    有人知道哪一个是最佳实践还是最标准的实践


  • 请注意,我不是在寻找为什么不尝试“此库”或“此工具”之类的答案,我的目标是练习和理解SFINAE,任何线索和建议都是好的。

    1.您应该阅读C++11中的POD概念。POD类型元素数组或指向POD类型元素的指针都是POD类型

    例如,下面的代码可以很好地编译

    2.像这样的东西可以帮你

    template<typename T>
    struct is_string
    {
       enum
       {
          value = false
       };
    };
    
    template<typename Char, typename Traits, typename Alloc>
    struct is_string<std::basic_string<Char, Traits, Alloc>>
    {
       enum
       {
          value = true
       };
    };
    
    模板
    结构是字符串
    {
    枚举
    {
    值=假
    };
    };
    模板
    结构是字符串
    {
    枚举
    {
    值=真
    };
    };
    
    功能

    // Iterable class specialization.
    template <typename T> typename std::enable_if<is_iterable<T>::value && !is_string<T>::value, size_t>::type SizeOf(const T &aValue)
    {
        size_t Result = sizeof(aValue);
    
        for (typename T::const_iterator I = aValue.begin(); I != aValue.end(); ++I)
        {
            Result += SizeOf(*I);
        }
    
        return Result;
    }
    
    template <typename T> typename std::enable_if<is_string<T>::value, size_t>::type SizeOf(const T& aValue)
    {
       return sizeof(aValue) + sizeof(typename T::value_type) * aValue.length();
    }
    
    //Iterable类专门化。
    模板typename std::enable_if::type SizeOf(const T&aValue)
    {
    大小\u t结果=sizeof(aValue);
    for(typename T::const_迭代器I=aValue.begin();I!=aValue.end();++I)
    {
    结果+=尺寸(*I);
    }
    返回结果;
    }
    模板typename std::enable_if::type SizeOf(const T&aValue)
    {
    返回sizeof(aValue)+sizeof(typename T::value_type)*aValue.length();
    }
    
    3.标准中没有这样的信息,
    sizeof(long)
    永远不应该等于
    sizeof(char)
    ,但是
    sizeof(char)
    不能等于
    sizeof(char[2])
    ,所以我认为第二种变体更可取。

    关于问题3,我认为在C++11中,它更干净(更清晰)使用而不是
    sizeof
    来获取诸如
    std::true\u type
    std::false\u type
    之类的

    例如,您的
    是可编辑的

    #include <type_traits> // std::true_type, std::false_type
    
    // Iterable class detector
    template <typename T> class is_iterable {
      template <typename U> static std::true_type test(typename U::const_iterator *);
      template <typename U> static std::false_type test(...);
    
    public:
       // Using decltype in separate typedef because of gcc 4.6 bug:
       // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709
       typedef decltype(test<T>(0)) result_type;
       static const bool value = result_type::value;
    };
    
    #包括//std::true\u类型、std::false\u类型
    //可调类检测器
    模板类是不可编辑的{
    模板静态std::true_类型测试(typename U::const_iterator*);
    模板静态标准::假_型测试(…);
    公众:
    //由于gcc 4.6错误,在单独的typedef中使用decltype:
    // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709
    typedef decltype(测试(0))结果类型;
    静态常量布尔值=结果类型::值;
    };
    
  • 你对指针等的“专门化”实际上不是专门化。它们超载了

  • 编译器首先执行重载解析,然后才检查专门化。在形式上不存在“模棱两可的专门化”。您的案例2、3和4在重载解析中已经失败,这正是因为您没有专门化

  • 重载解析仅取决于参数类型。重载只在返回类型上有所不同。当然,某些重载可能被禁用,但您需要禁用除一个重载之外的所有重载。目前,吊舱阵列可同时启用吊舱和阵列过载

  • 对于容器,更好的解决方案可能是使用
    container.size()

  • char[2]
    是首选,因为根据标准,
    sizeof(long)
    可以是1

  • 有一个问题我不会问,我会回答,那就是“我应该如何编写数组重载”?诀窍在于对数组的引用:

    template<typename T, unsigned N> 
    constexpr size_t SizeOf(const T (&aValue)[N])
    {
      // return N * sizeof(T); If you want to do the work yourself
      return sizeof(aValue); // But why bother?
    }
    
    模板
    constexpr size\u t SizeOf(const&aValue)[N])
    {
    //返回N*sizeof(T);如果您想自己做这项工作
    return sizeof(aValue);//但是为什么要麻烦呢?
    }
    
    1)。因为数组名-它是指向第一个单元格的指针。它们被认为是指针。所以你有一个模棱两可的电话。我建议您对指针使用另一个版本的重载
    SizeOf
    。使用
    sizeof(\u-array)/sizeof(\u-array[0])计算数组大小更好。
    。2). 在这种情况下,应使用模板参数作为模板。例如:
    template std::enable_if::type SizeOf(const T&\u value)
    在其中可以声明变量
    容器
    。它将专门用于std::basic_string.3)。没有区别,因为long和char的大小不同(我希望在所有编译器中都是如此)。@Pie_Jesu:不,数组名不是指向其第一个元素的指针。信息技术
    #include <type_traits> // std::true_type, std::false_type
    
    // Iterable class detector
    template <typename T> class is_iterable {
      template <typename U> static std::true_type test(typename U::const_iterator *);
      template <typename U> static std::false_type test(...);
    
    public:
       // Using decltype in separate typedef because of gcc 4.6 bug:
       // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709
       typedef decltype(test<T>(0)) result_type;
       static const bool value = result_type::value;
    };
    
    template<typename T, unsigned N> 
    constexpr size_t SizeOf(const T (&aValue)[N])
    {
      // return N * sizeof(T); If you want to do the work yourself
      return sizeof(aValue); // But why bother?
    }