【问题标题】:compile-time function choice depending on type size编译时函数选择取决于类型大小
【发布时间】:2019-11-15 23:12:49
【问题描述】:

我想要一个模板函数以一种特殊的方式复制数据。 如果数据元素类型大小是 4 字节的倍数,则有一种简单的方法,即 (sizeof(T) % 4 == 0):

template <typename T, typename Idx, uint32 dimensions>
void loadData4BWords(T *target, const T *source, const Idx eleCount);

如果不是这种情况,还有一种更复杂的方法来复制数组:

template <typename T, typename Idx, uint32 dimensions>
void loadDataNo4BWords(T *target, const T *source, const Idx eleCount);

如何编写一个调用者模板函数,在编译时做出这个决定并对用户透明?例如:

template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount);

应该根据编译时条件multipleOf4BWord = (sizeof(T) % 4 == 0)调用上述两个版本之一。更准确地说,loadData 应该在编译时被翻译成上述两个版本之一。

【问题讨论】:

  • 什么 C++ 版本可用?
  • GCC 7 和 MSVC 19.23 支持的交集。

标签: c++ templates template-specialization compile-time


【解决方案1】:

自 C++17 起,您可以使用 if constexpr 调用其中一个:

template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount) {
    if constexpr(sizeof(T) % 4 == 0)
        loadData4BWords<T, Idx, dimensions>(target, source, eleCount);
    else
        loadDataNo4BWords<T, Idx, dimensions>(target, source, eleCount);
}

if 相比,if constexpr 在编译时进行测试,只编译匹配的分支。

【讨论】:

  • 感谢您的快速响应和这个不错的解决方案! :)
【解决方案2】:

if constexpr 是最好的。但是老派标签调度也有效,并且在某些情况下可能会更清晰(特别是 C++17 之前的版本),所以我将在讨论中贡献这个选项:

template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount, std::true_type)
{
    loadData4BWords(target, source, eleCount);
}

template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount, std::false_type)
{
    loadDataNo4BWords(target, source, eleCount);
}

template <typename T, typename Idx, uint32 dimensions>
void loadData(T *target, const T *source, const Idx eleCount)
{
    loadData(target, source, eleCount,
        std::integral_constant<bool, sizeof(T) % 4 == 0>{});
}

【讨论】:

    【解决方案3】:

    您可以使函数成为结构的成员并使用部分模板特化:

    template<typename T, bool is_even_multiple=0==(sizeof(T)%4)>
    struct load_data_helper;
    
    template<typename T>
    struct load_data_helper<T, true>
    {
    template<uint32_t Dimensions, typename Idx>
    static void apply(T * dest, T const * src, Idx const & index)
    { ... }
    };
    template<typename T>
    struct load_data_helper<T, false>
    {
    template<uint32_t Dimensions, typename Idx>
    static void apply(T * dest, T const * src, Idx const & index)
    { ... }
    };
    
    template<uint32_t Dimensions, typename T, typename Idx>
    void load_data(T * dest, T const * src, Idx const & index)
    {
    load_data_helper<T>::apply<Dimensions>(dest, src, index);
    }
    

    然后调用将是:

    load_data<3>(dest, src, index);
    

    注意我并没有实际编译上面的代码,所以提供的代码可能有错误,但概述的方法应该可以工作。

    【讨论】:

    • 感谢您的快速响应!如果我不能使用 constexpr if,我会寻求这样的解决方案。
    【解决方案4】:

    在 C++17 if constexpr 中,正如不均匀标记所建议的那样,是最简单和更清晰的解决方案 (恕我直言)。

    在 C++17(C++11 和 C++14)之前,您可以使用重载和 SFINAE(使用 std::enable_if

    我的意思是...你可以简化很多问题,如果,而不是 loadData4BWords()loadDataNo4BWords() 功能曾经启用,你创建一个 loadData() 仅当 0u == sizeof(T) % 4u 启用(loadData4BWords() 等效)和loadData() 仅在 0u != sizeof(T) % 4uloadDataNo4BWords() 等效项)时启用。

    以下是完整的 C++11 工作示例(简化:只有一个参数)

    #include <iostream>
    #include <type_traits>
    
    template <typename T>
    typename std::enable_if<0u == sizeof(T) % 4u>::type loadData (T *)
     { std::cout << "4 version" << std::endl; }
    
    template <typename T>
    typename std::enable_if<0u != sizeof(T) % 4u>::type loadData (T *)
     { std::cout << "no 4 version" << std::endl; }
    
    
    int main ()
     {
       char  ch;
       int   i;
    
       loadData(&ch);
       loadData(&i);
     }
    

    在 C++14(和 C++17,如果您愿意)中,您可以使用 std::enable_if_t 来简化一点

    template <typename T>
    std::enable_if_t<0u == sizeof(T) % 4u> loadData (T *)
     { std::cout << "4 version" << std::endl; }
    
    template <typename T>
    std::enable_if_t<0u != sizeof(T) % 4u> loadData (T *)
     { std::cout << "no 4 version" << std::endl; }
    

    ps:另请参阅 Jeff Garrett 的回答中的标签调度方式。

    【讨论】:

      猜你喜欢
      • 2012-04-19
      • 2010-09-23
      • 2015-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多