编译时函数选择取决于类型大小
Posted
技术标签:
【中文标题】编译时函数选择取决于类型大小【英文标题】: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 支持的交集。 【参考方案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) % 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;
ps:另请参阅 Jeff Garrett 的回答中的标签调度方式。
【讨论】:
以上是关于编译时函数选择取决于类型大小的主要内容,如果未能解决你的问题,请参考以下文章
c ++ unordered_set .find方法不会编译,给出“表达式必须有类类型”的错误
定义表示不透明 C 结构的 Rust 类型的交叉编译安全方法,其大小在编译时已知