我可以禁止临时对象作为参数吗?

Can I forbid temporary objects as parameters?

假设我有以下功能:

void foo(Object& o) {
    /* only query o, dont alter it*/
}

是否可以仅使用已构建的对象调用此函数,并且如果我使用临时对象调用该函数,Visual Studio 会抛出编译错误?

struct Object {
    /*Members*/
}

void foo(Object& o) {
    /* only query o, dont alter it*/
}

int main() {
    Object o = Object();
    foo(o); // allow this
    foo(Object()) // but disallow this
}

如果您的参数不是 const,该函数将不接受临时参数。

如果您的参数是 const,该函数接受临时对象和常规对象。

但是如果你想阻止这种情况,你可以使用下面的方法

struct Object{};
void foo(const Object& o) {
    /*only query o, don't alter it*/
}
void foo(Object&& ) = delete;

int main() {
    Object o;
    foo(o); // allow this
    foo(Object{}); // but disallow this
}

Live

明确删除 const && 重载

临时对象可以通过将其绑定到 const 左值引用(在函数调用中)来延长其生命周期,而它不能绑定到非 const 左值引用。这意味着您的原始示例实现了所寻求的行为(不能用临时对象调用),但代价是使参数成为非const(即使实现只查询而不改变对象)。这可以说违反了 const 正确性。

由于您的自由函数 API 正在检查 Object 对象,您可以考虑将其更改为成员函数并使用 ref-qualifiers 显式删除将由重载决议选择的重载对于临时对象。第一种方法可能是简单地删除 && 重载:

struct Object {
    // ...
    
    void foo() const & {}
    void foo()       && = delete;
};

int main() {
    Object o = Object();
    const Object co = Object();

    o.foo();
    co.foo();
    
    //Object().foo();  // error: use of deleted function
}

但是,这并不禁止 const 临时对象 以及 可从 const 对象移动 的情况,尽管有些做作( const xvalues),因为已删除的非 const 右值引用限定符重载对于 const 右值参数不可行:

std::move(co).foo();  // Accepted.
static_cast<const Object&&>(Object()).foo();  // Accepted.

因此,我们可以通过显式删除 const && 重载来移除极端情况,而不是显式删除 && 重载,因为这也是非const 个临时对象:

struct Object {
    // ...
    
    void foo() const & {}
    void foo() const && = delete;
};

int main() {
    Object o = Object();
    const Object co = Object();

    o.foo();
    co.foo();
    
    //std::move(o).foo();   // error: use of deleted function
    //std::move(co).foo();  // error: use of deleted function

    //Object().foo();  // error: use of deleted function
    //static_cast<const volatile Object&&>(Object()).foo();  // error: use of deleted function
}

我们可能会注意到使用了相同的方法,例如对于 std::reference_wrapperstd::refstd::cref 辅助函数;来自 [functional.sym]:

// [refwrap], reference_­wrapper
// ...
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

// ...

因为您自然要删除临时对象的引用包装器。