编译时函数选择取决于类型大小
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) % 4u
(loadData4BWords()
等效)和 loadData()
仅在 0u != sizeof(T) % 4u
(loadDataNo4BWords()
等效)时启用。
以下是完整的 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 的回答中的标签调度方式。
我想要一个模板函数以特殊方式复制数据。 如果数据元素类型大小是 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) % 4u
(loadData4BWords()
等效)和 loadData()
仅在 0u != sizeof(T) % 4u
(loadDataNo4BWords()
等效)时启用。
以下是完整的 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 的回答中的标签调度方式。