减少模板化变差函数歧义的最佳方法是什么
What is the best way to reduce ambiguity for templated variadic functions?
我目前正在尝试制作一个名为MakeByte
的可变模板函数,该函数将接受任意数量的位参数,并将它们放入一个字节中。以下是它的用法示例:
// The Wii U has 4 RAM chips, here we select a seemingly "random" one using an
// algorithm to generate one from the coordinates.
quint32 bank_bit_0 = Bit((y / (16 * m_num_pipes)) ^ x_3);
quint32 bank_bit_1 = Bit((y / (8 * m_num_pipes)) ^ x_4);
quint32 bank = MakeByte(bank_bit_0, bank_bit_1);
我有三个功能,在一个单独的标题中涉及:
template <typename T1, typename... T2> T1 MakeByte(T1 bit, T2... bits)
,调用递归函数的外部代码将使用的函数template <typename T1, typename... T2> T1 MakeByte(T1 byte, quint32 pos, T1 bit, T2... bits)
,对每个比特进行迭代的递归函数。这个函数有额外的参数来跟踪最后一个字节,以及放置下一个位的当前位置template <typename T1, typename T2> T1 MakeByte(T1 byte, quint32 pos, T2 bit)
,处理最后一位的函数
以下是完整的3个定义:
template <typename T1, typename T2>
constexpr T1 MakeByte(T1 byte, quint32 pos, T2 bit)
{
return byte | (bit << pos);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 byte, quint32 pos, T1 bit, T2... bits)
{
return MakeByte(byte | (bit << pos), pos + 1, bit, bits...);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
return MakeByte(static_cast<T1>(0), 0, bit, bits...);
}
问题是,当使用g++编译时,我会得到以下错误:
/home/kyle/Documents/Projects/C++/Qt/MK8Studio/Source/Common.h:44: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return MakeByte(static_cast<T1>(0), 0, bit, bits...);
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在这一点上,我重命名了两个递归函数,以防出现歧义:
template <typename T1, typename T2>
constexpr T1 MakeByte_(T1 byte, quint32 pos, T2 bit)
{
return byte | (bit << pos);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte_(T1 byte, quint32 pos, T1 bit, T2... bits)
{
return MakeByte_(byte | (bit << pos), pos + 1, bit, bits...);
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
return MakeByte_(static_cast<T1>(0), 0, bit, bits...);
}
这段代码确实可以编译,但我忍不住觉得这有点像黑客。从设计的角度来看,减少可变模板函数中歧义的最佳方法是什么?
也许这是个人品味的问题,但我更喜欢避免有两个版本的函数在做同样的事情(在这种情况下:shift和or)。
我建议写一个终端案例,简单地返回一个值(无偏移,无或)
template <typename T1>
constexpr T1 MakeByte_ (T1 byte, quint32)
{ return byte; }
和递归情况
template <typename T1, typename... T2>
constexpr T1 MakeByte_(T1 byte, quint32 pos, T1 bit, T2... bits)
{ return MakeByte_(byte | (bit << pos), pos + 1, bit, bits...); }
这避免了歧义,因为如果MakeByte_
接收到三个或多个参数,则调用递归版本;如果接收到两个参数,则称为最终版本。
在您的案例中,有明显不同的方法,如
quint32 bank_bit_0 = Bit((y / (16 * m_num_pipes)) ^ x_3);
quint32 bank_bit_1 = Bit((y / (8 * m_num_pipes)) ^ x_4);
quint32 bank_bit_2 = Bit((y / (m_num_pipes)) ^ x_5);
quint32 bank = MakeByte(bank_bit_0, bank_bit_1, bank_bit_1);
会调用你的助手函数
template <typename T1, typename T2>
constexpr T1 MakeByte(T1 byte, quint32 pos, T2 bit)
注意:在C++17中,您甚至可以编写代码:
template <typename Tuple, std::size_t ... Is>
constexpr auto MakeByte_(const Tuple& tuple, std::index_sequence<Is...>)
{
return ((std::get<Is>(tuple) << Is) | ...) ;
}
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
return MakeByteHelper(std::make_tuple(bit, bits...),
std::make_index_sequence<1 + sizeof...(T2)>());
}
多亏了Codeing Den Discord服务器上的eracpp
,我使用了一个折叠表达式:
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits)
{
T1 byte = bit;
quint32 pos = 0;
((byte |= ((bits & 1) << ++pos)), ...);
return byte;
}
这将代码缩减为一个函数,但确实需要C++17。这将从左到右的参数作为LSB到MSB,但这可以用于从左到右侧作为MSB到LSB(与二进制布局相关):
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits) {
uint32_t pos = sizeof...(bits);
T1 byte = bit;
((byte = (byte << 1) | bits), ...);
return byte;
}
最后,他们提出了一个我还没有测试过的非C++17解决方案:
template <typename T1, typename... T2>
constexpr T1 MakeByte(T1 bit, T2... bits) {
T1 byte = bit;
using init_t = int[];
(void)init_t{((byte = (byte << 1) | bits), 0)...};
return byte;
}
相关文章:
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 构造对象的歧义
- 在c代码之间共享数据的最佳方式
- 使用std::source_location报告错误的最佳实践
- 派生类销毁的最佳实践是什么
- 将寄存器设计成可由C和C++访问的外设的最佳实践
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 在C++中向零方向近似的最佳方法
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- 如果条件为TRUE(最佳方式?),则在do while循环中后置增量
- 检测win32服务创建和删除的最佳方法
- 在reactor中存储eventHandlers的最佳方式是什么
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 减少模板化变差函数歧义的最佳方法是什么
- 常量最佳匹配函数与其他函数之间的歧义