我可以禁止临时对象作为参数吗?
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
}
明确删除 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_wrapper
的 std::ref
和 std::cref
辅助函数;来自 [functional.sym]:
// [refwrap], reference_wrapper
// ...
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
// ...
因为您自然要删除临时对象的引用包装器。
假设我有以下功能:
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
}
明确删除 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_wrapper
的 std::ref
和 std::cref
辅助函数;来自 [functional.sym]:
// [refwrap], reference_wrapper // ... template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete; // ...
因为您自然要删除临时对象的引用包装器。