5

假设我有这个功能:

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

2 回答 2

10

如果您的参数不是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
}

居住

于 2020-07-20T08:36:02.500 回答
4

显式删除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::refstd::cref辅助函数std::reference_wrapper。来自[functional.sym]

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

// ...

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

于 2020-07-20T08:45:22.647 回答