编译时函数选择取决于类型大小

compile-time function choice depending on type size

我想要一个模板函数以特殊方式复制数据。 如果数据元素类型大小是 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++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在编译时进行测试,只编译匹配的分支。

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

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);

请注意,我实际上并没有编译以上内容,因此提供的代码中可能存在错误,但概述的方法应该有效。

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>{});
}

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

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

我的意思是......你可以简化很多问题,而不是启用 loadData4BWords()loadDataNo4BWords() 功能,你创建一个 loadData() 仅当 0u == sizeof(T) % 4uloadData4BWords() 等效)和 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; }

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