C++ 如何将函数标记为使其参数无效

C++ 如何将函数标记为使其参数无效,c++,c++11,types,move-semantics,rvalue-reference,C++,C++11,Types,Move Semantics,Rvalue Reference,我有一个函数f,它接受指针向量。函数f完成后,这些指针将不再有效。注意,实际上不需要更改向量本身,我只是想鼓励调用方在调用f之后不要使用指针。f有三种可能的签名: 移动签名 void f(vector<void*> &&v); // because the pointers in v are no longer valid. // This signature also allows me to have f call clear() on v. void f(c

我有一个函数
f
,它接受指针向量。函数
f
完成后,这些指针将不再有效。注意,实际上不需要更改向量本身,我只是想鼓励调用方在调用
f
之后不要使用指针。
f
有三种可能的签名:

移动签名

void f(vector<void*> &&v); // because the pointers in v are no longer valid. 
// This signature also allows me to have f call clear() on v.
void f(const vector<void*> &v); // because the pointers in v are no longer valid,
// but we don't have to change the vector v.
void f(vector<void*> *v); // The functino modifies v in a predictable way 
// (it clears it). A pointer is used instead of a reference so that
// calls to the function will have a '&' which clearly shows to the reader
// that this function modifies its argument. '*' is different from '&&' since '&&' 
// may imply "do not use v, but it is unknown how it will be modified" while 
// '*' implies a clear semantic for how v is changed.
void f(向量&v);//因为v中的指针不再有效。
//这个签名还允许我在v上使用f call clear()。
常量签名

void f(vector<void*> &&v); // because the pointers in v are no longer valid. 
// This signature also allows me to have f call clear() on v.
void f(const vector<void*> &v); // because the pointers in v are no longer valid,
// but we don't have to change the vector v.
void f(vector<void*> *v); // The functino modifies v in a predictable way 
// (it clears it). A pointer is used instead of a reference so that
// calls to the function will have a '&' which clearly shows to the reader
// that this function modifies its argument. '*' is different from '&&' since '&&' 
// may imply "do not use v, but it is unknown how it will be modified" while 
// '*' implies a clear semantic for how v is changed.
void f(常数向量&v);//因为v中的指针不再有效,
//但我们不需要改变向量v。
指针签名

void f(vector<void*> &&v); // because the pointers in v are no longer valid. 
// This signature also allows me to have f call clear() on v.
void f(const vector<void*> &v); // because the pointers in v are no longer valid,
// but we don't have to change the vector v.
void f(vector<void*> *v); // The functino modifies v in a predictable way 
// (it clears it). A pointer is used instead of a reference so that
// calls to the function will have a '&' which clearly shows to the reader
// that this function modifies its argument. '*' is different from '&&' since '&&' 
// may imply "do not use v, but it is unknown how it will be modified" while 
// '*' implies a clear semantic for how v is changed.
void f(向量*v);//functino以可预测的方式修改v
//(它清除了它)。使用指针而不是引用,以便
//对函数的调用将有一个“&”,它清楚地显示给读者
//此函数修改其参数。“*”与“&&”不同,因为“&&”
//可能暗示“不要使用v,但不知道如何修改它”
//“*”意味着v如何变化的明确语义。
在C++11中使用哪个签名更为惯用?

如何

void f(vector<void*> v);
void f(向量v);
使用它:

vector<void*> myVec = /*...*/;
f(std::move(myVec));
向量myVec=/*…*/; f(std::move(myVec)); 如果
f
在逻辑上需要向量的所有权,这就是惯用的方法。它允许调用者决定是将向量移动还是复制到
f


如果调用者真的希望
f
修改他的向量(因此向量实际上是一个in/out参数),那么这不适合您的需要。然而,输入/输出参数很糟糕。函数应将输入作为参数,并将输出作为返回值返回。这就是上帝的本意。

在这三个选项中:
vector*
需要一个左值来表示地址<代码>常量向量&允许传入左值或右值<代码>向量&只允许传入右值

根据您的问题,使用左值或右值调用函数是有意义的,因此
const vector&
是一个明显的选择


无法通过类型系统指示调用方应该停止使用包含的指针,您不应该尝试通过类型系统指示。通过文档说明这一点。

简短的回答:没有办法做到这一点。唯一的“官方”是相反的:有一个签名保证函数
f(…)
不会改变它的参数:
const
关键字

通常情况下,应遵守以下规定:

  • 不修改其参数的函数要么将其参数作为值复制,要么用
    const

  • 通过非常量引用、移动或指向非常量对象的指针传递的参数应理解为“此参数很可能被调用函数
    f(…)
    修改”


正如其他人所说,类型系统不允许您指示类似“此函数调用后不要使用此数据”的内容。你能做的是:

void f(向量和向量)
{
//…使用v。。。
v、 clear();//鼓励调用方在调用后不要使用指针
}

f
如果向量正在删除指针(或释放它们的句柄),则应清除该向量。给调用者留下一个intederminate值向量是毫无意义的危险


因此,
f
应该通过非常量引用接受向量。您想做这个左值引用还是右值引用取决于您;但是左值版本似乎更简单。

如果您真的想在类型系统中实现这一点,那么总是可以使用您自己的类型对额外信息进行编码

template<class T>
struct invalidates_contained_pointers;

template<class T>
invalidates_contained_pointers<T>* contents_will_be_invalidated(T* ptr) {
    return reinterpret_cast<invalidates_contained_pointers<T>*>(ptr);
}

void f(invalidates_contained_pointers<vector<void*>> *v){
    auto pv = reinterpret_cast<vector<void*> *>(v);
    // ...
}

f(contents_will_be_invalidated(&vec));
模板
结构使包含的指针失效;
模板
无效包含的指针*内容将无效(T*ptr){
返回重新解释(ptr);
}
void f(使包含的指针无效*v){
自动pv=重新解释铸件(v);
// ...
}
f(内容将被作废(&vec));

类似的方法也可用于参考。

为什么这比
和&
版本更好,这又如何比我的其他建议更好?我试图理解这三个向量中哪一个是最好的,以及为什么向量是包含指针/句柄/任何东西的向量。实际上没有必要修改它,但我想向调用者澄清,他不应该再使用这些指针/句柄。如果
f
删除指针,那么这是最糟糕的选择(如果调用者忘记写入
std::move
,则会给调用者留下一个不确定指针向量)您应该添加一个删除的左值重载,以防止意外不移动。
const vector&
根本不允许您修改向量,只有在“无参数”是合理参数的情况下,传递指针才有意义。第一个变体不接受左值,这对我们来说很奇怪。只需传递值,让调用者决定是移动还是复制。这些签名实际上都没有说明/保证向量内部的指针(更不用说指针对象了),只是关于向量本身。@KonradRudolph—
&&
签名在语义上意味着“这个函数可以将
v
修改为所有可破坏的内容,所以不要依赖于v的内容”,这很接近,你不这么认为吗?@tohava不。正如你所说,签名上写着“不要依赖向量的内容”但这与此无关。如果调用方有指向同一资源的不同指针怎么办?如果指针表示所有权(
unique\u ptr
),那么您的签名就有意义了。@tohava问题并不是说这并不完美,而是在积极误导:没有人会这么做