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

compile-time function choice depending on type size

本文关键字:类型 取决于 选择 函数 编译      更新时间:2023-10-16

我想要一个模板函数来以特殊方式复制数据。如果数据元素类型大小是4字节的倍数。,(大小为(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应该在编译时被翻译成上面两个版本中的一个。

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

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在编译时进行测试,并且只编译匹配的分支。

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

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

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

注意,我还没有真正编译过上面的代码,所以提供的代码中可能有错误,但概述的方法应该可以工作。

在C++17中,正如uneven_mark所建议的,if constexpr是最简单、更清晰的解决方案(IMHO(。

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

我的意思是。。。如果不启用loadData4BWords()loadDataNo4BWords()函数,而是创建仅在0u == sizeof(T) % 4u(等效loadData4BWords()(时启用的loadData()和仅在0u != sizeof(T) % 4u(等效loadDataNo4BWords()(时启用loadData(),则可以大大简化问题。

以下是完整的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; }

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