避免过载解析中的歧义

时间:2019-05-06 05:58:08

标签: parameters c++17 ambiguous

这是对this的后续问题,因此,如果您需要查看Register类,请参考该问题。现在,根据提供的答案,我编写了一个函数来执行此操作。我有两个版本的函数,一个将结果存储回原始版本,另一个将返回副本。这是我的功能:

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

第一个保存值,第二个返回副本。我的问题是关于2 nd 函数的,我最终添加了第二个未使用的参数,以避免由于重载解析而造成的歧义,因为不能仅对返回类型进行函数解析。因此,当我调用此函数时,必须将01truefalse传递给无效的函数。

总的来说,这本身并不是什么大问题,但是,对我而言,这似乎并不十分简洁。还有其他方法可以做到这一点吗?我不想让它成为班级的职能。我的Register类或结构是完整的,并且对寄存器执行的任何类型的操作都将由引用一个或多个寄存器对象的函数完成。

2 个答案:

答案 0 :(得分:1)

您可以使用std::optional来实现。

功能模板setSelectedTextRange的{​​{1}}类型应为return

功能模板应修改为:

reverseBitOrder

Live Demo here

但是您实际上并不需要使用std::optional<vpc::Register<N>>,因为函数模板中实际上没有“失败”的情况。

答案 1 :(得分:0)

如果我理解正确,则两个函数都会计算x并将其存储在Register<N>中,但是一个函数按值返回所述对象,另一个函数将结果存储在函数的参数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);

尽管我不建议这样做,但麻烦多于其应有的价值。我建议使用我称为“正确方法”的解决方案。它最简单,以后最难使用的错误