避免重载决议中的歧义
Avoiding ambiguity in overload resolution
这是 问题的后续,因此如果您需要查看注册表 class,请参阅该问题。现在,根据提供的答案,我编写了一个函数来做到这一点。我有 2 个版本的函数,一个将结果存储回原始版本,另一个将 return 复制一份。这是我的功能:
template<std::uint64_t N>
void reverseBitOrder( Register<N>& reg ) {
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
auto x = vpc::Byte(str);
reg.register_ = x;
}
// Extra unused parameter to avoid ambiguity
template<std::uint64_t N>
Register<N> reverseBitOrder(Register<N>& reg, bool _x_ /*not used*/ ) {
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
auto x = vpc::Byte(str);
Register<N> temp;
temp.register_ = x;
return temp;
}
第一个保存值,第二个return保存一个副本。我的问题是关于 2nd 函数我最终添加了第二个未使用的参数,以避免由于重载解析导致的歧义,因为函数无法在 return 上解析单独类型。因此,当我调用此函数时,我必须将 0
、1
、true
或 false
传递给无效的函数。
总的来说,这本身并不是什么大问题,但是,对我来说,它似乎不是很干净和简洁。还有其他方法可以实现这一目标吗?我不希望将其作为 class 的函数。我的寄存器 class 或结构是完整的,在寄存器上完成的任何类型的操作都将由引用一个或多个寄存器对象的函数完成。
您可以使用 std::optional
来实现。
函数模板reverseBitOrder
的return
类型应该是std::optional<vpc::Register<N>>
。
函数模板修改为:
template<std::uint64_t N>
std::optional<vpc::Register<N>> reverseBitOrder(vpc::Register<N>& reg, bool _x_ = false) {
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
vpc::Register<N> temp;
if(_x_) //return copy
{
temp.register_ = vpc::Byte(str); //since register_ is a vpc::Byte. Generalize accordingly.
return temp;
}
else //save in the same variable
{
reg.register_ = vpc::Byte(str);
return {};
}
}
但是你真的不需要使用std::optional
,因为函数模板中确实没有"failure"的情况。
如果我理解正确,两个函数都计算 x
并将其存储在 Register<N>
中,但是一个 returns 按值表示对象,而另一个将结果存储在函数的参数中reg
.
技术上,这可以通过定义这两个函数来重载 constness 来完成:
template<std::uint64_t N> void reverseBitOrder( Register<N>& reg );
template<std::uint64_t N> Register<N> reverseBitOrder( Register<N> const& reg );
虽然这在技术上回答了您的问题,但那将是糟糕的设计。
如果我没记错的话,真正的问题是你想要的是这个:
// Behaviour 1: value stored in reg
reverseBitOrder(reg);
// Behaviour 2: value stored in val, reg left untouched
auto val = reverseBitOrder(reg);
问题是返回值是否被使用不是你可以从函数内部检测到的。
让一个函数在这里做两件事的正确方法是使用具有此签名的函数:
template<std::uint64_t N> void reverseBitOrder( Register<N> const& inputReg, Register<N>& outputReg );
该函数将使用 inputReg
计算 x
,然后将结果存储在 outputReg
中,这意味着您可以像这样使用它:
// Behaviour 1: value stored in reg
reverseBitOrder(reg, reg);
// Behaviour 2: value stored in val, reg leftuntouched
reverseBitOrder(reg, val);
现在,如果 真的 不适合你,还有 获得语法糖的方法 你寻找,以不必要的复杂性和向 Register<N>
添加构造函数为代价。它看起来大致像这样:
// Forward declaration
template<std::uint64_t N>
class Register;
// Proxy class
template<std::uint64_t N>
struct RegisterProxy
{
Register<N>* reg;
TypeOfX x;
~RegisterProxy()
{
if (reg)
{
reg->register = x;
}
}
};
// Modified Register class
template<std::uint64_t N>
class Register
{
...
Register(RegisterProxy& proxy)
{
...
register = proxy.x;
proxy.reg = nullptr;
...
}
...
// Define the matching assignment operator too
...
};
// Your function
template<std::uint64_t N>
RegisterProxy<N> reverseBitOrder( Register<N>& reg )
{
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
auto x = vpc::Byte(str);
return RegisterProxy{reg, x};
}
这让你可以做到
// Behaviour 1: temporary object immediately destroyed and value stored in reg
reverseBitOrder(reg);
// Behaviour 2: constructor called, value stored in val
// the temporary object's destructor doesn't do anything, reg left untouched
auto val = reverseBitOrder(reg);
虽然我不建议这样做,但麻烦多于它的价值。我建议使用我称为 "the proper way" 的解决方案。最简单的也是最难用的,后面用错了
这是
template<std::uint64_t N>
void reverseBitOrder( Register<N>& reg ) {
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
auto x = vpc::Byte(str);
reg.register_ = x;
}
// Extra unused parameter to avoid ambiguity
template<std::uint64_t N>
Register<N> reverseBitOrder(Register<N>& reg, bool _x_ /*not used*/ ) {
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
auto x = vpc::Byte(str);
Register<N> temp;
temp.register_ = x;
return temp;
}
第一个保存值,第二个return保存一个副本。我的问题是关于 2nd 函数我最终添加了第二个未使用的参数,以避免由于重载解析导致的歧义,因为函数无法在 return 上解析单独类型。因此,当我调用此函数时,我必须将 0
、1
、true
或 false
传递给无效的函数。
总的来说,这本身并不是什么大问题,但是,对我来说,它似乎不是很干净和简洁。还有其他方法可以实现这一目标吗?我不希望将其作为 class 的函数。我的寄存器 class 或结构是完整的,在寄存器上完成的任何类型的操作都将由引用一个或多个寄存器对象的函数完成。
您可以使用 std::optional
来实现。
函数模板reverseBitOrder
的return
类型应该是std::optional<vpc::Register<N>>
。
函数模板修改为:
template<std::uint64_t N>
std::optional<vpc::Register<N>> reverseBitOrder(vpc::Register<N>& reg, bool _x_ = false) {
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
vpc::Register<N> temp;
if(_x_) //return copy
{
temp.register_ = vpc::Byte(str); //since register_ is a vpc::Byte. Generalize accordingly.
return temp;
}
else //save in the same variable
{
reg.register_ = vpc::Byte(str);
return {};
}
}
但是你真的不需要使用std::optional
,因为函数模板中确实没有"failure"的情况。
如果我理解正确,两个函数都计算 x
并将其存储在 Register<N>
中,但是一个 returns 按值表示对象,而另一个将结果存储在函数的参数中reg
.
技术上,这可以通过定义这两个函数来重载 constness 来完成:
template<std::uint64_t N> void reverseBitOrder( Register<N>& reg );
template<std::uint64_t N> Register<N> reverseBitOrder( Register<N> const& reg );
虽然这在技术上回答了您的问题,但那将是糟糕的设计。 如果我没记错的话,真正的问题是你想要的是这个:
// Behaviour 1: value stored in reg
reverseBitOrder(reg);
// Behaviour 2: value stored in val, reg left untouched
auto val = reverseBitOrder(reg);
问题是返回值是否被使用不是你可以从函数内部检测到的。
让一个函数在这里做两件事的正确方法是使用具有此签名的函数:
template<std::uint64_t N> void reverseBitOrder( Register<N> const& inputReg, Register<N>& outputReg );
该函数将使用 inputReg
计算 x
,然后将结果存储在 outputReg
中,这意味着您可以像这样使用它:
// Behaviour 1: value stored in reg
reverseBitOrder(reg, reg);
// Behaviour 2: value stored in val, reg leftuntouched
reverseBitOrder(reg, val);
现在,如果 真的 不适合你,还有 获得语法糖的方法 你寻找,以不必要的复杂性和向 Register<N>
添加构造函数为代价。它看起来大致像这样:
// Forward declaration
template<std::uint64_t N>
class Register;
// Proxy class
template<std::uint64_t N>
struct RegisterProxy
{
Register<N>* reg;
TypeOfX x;
~RegisterProxy()
{
if (reg)
{
reg->register = x;
}
}
};
// Modified Register class
template<std::uint64_t N>
class Register
{
...
Register(RegisterProxy& proxy)
{
...
register = proxy.x;
proxy.reg = nullptr;
...
}
...
// Define the matching assignment operator too
...
};
// Your function
template<std::uint64_t N>
RegisterProxy<N> reverseBitOrder( Register<N>& reg )
{
auto str = reg.register_.to_string();
std::reverse(str.begin(), str.end());
auto x = vpc::Byte(str);
return RegisterProxy{reg, x};
}
这让你可以做到
// Behaviour 1: temporary object immediately destroyed and value stored in reg
reverseBitOrder(reg);
// Behaviour 2: constructor called, value stored in val
// the temporary object's destructor doesn't do anything, reg left untouched
auto val = reverseBitOrder(reg);
虽然我不建议这样做,但麻烦多于它的价值。我建议使用我称为 "the proper way" 的解决方案。最简单的也是最难用的,后面用错了