How to prevent an r-value
我正在创建这样一个函数:
1 2 3 4
| void SetItem(const Key &key, const Value &value)
{
...
} |
其中键和值是某种类型。
在内部,我希望像这样存储这对:
1
| std::pair<const Key &, Value> |
所以我的问题是:我需要强制该键实际上是一个L值,这样在函数退出时它就不会被清除(R值不安全)
我可以在函数上签名:
1
| void SetItem(Key &key, const Value &value) |
这将阻止使用r值,但它不允许使用const键,我也不喜欢。
在保持常量的同时,有没有一种方法可以强制键成为l值?
我可以创建一个R值过载来防止它:
1 2 3 4
| void SetItem(Key &&key, const Value &value)
{
[What do I put here?]
} |
谢谢
- 拥有L值并不能解决你的终身问题。
- 你是对的,但它至少有助于执行它。我希望键在某个地方是一个静态变量。
随着注释合并的改进,在完全符合C++ 11编译器的情况下,它应该是这样的:
1 2 3 4 5 6
| class X{
public:
void SetItem(Key const& key, Value const& value);
private:
void SetItem(Key const&&, Value const&) = delete;
}; |
私有过载将捕获所有Key个值。访问检查不是在过载解决过程中完成的,因此我们可以把它放在private下,这样可能的friend在编译时也会得到一个很好的错误消息,我们= delete它。
对于尚不支持显式删除函数的编译器,您可以将其保持为未定义状态,但对于可能的friends,这只会显示为链接器错误。但是,一般的访问群体会收到"setitem"is unaccessible"编译器错误消息。:)
- 我不知道= delete在非构造函数/析构函数上工作。
- 那么void SetItem(Key &&key, const Value &value);呢?
- @是的,就是这样。当有人试图调用这个函数时,他们会得到一个链接器错误。
- 令人惊叹的!我希望我可以使用= delete;,但不幸的是,我使用的是msvc,它不支持它。因此,我将坚持不执行它,并发表评论。
- 你也应该把它保密。
- @MRANZ编号:使用const Key&&。还是希望常量值接受左值重载!?
- @约翰内斯:很好的观点(尽管一般来说,康斯塔瓦勒斯是一个愚蠢的想法)。
- 你有常量r值的例子吗?对于我所看到的一切,江户十一〔四〕已经足够了。
- @mranz:Key const f();:这很愚蠢,因为它完全禁用了移动语义,但你永远不会知道。
- @Xeo啊,谢谢。幸运的是(?)我的开发人员同事不使用const,即使他们应该……
除了@xeo的答案,你也可以
1 2 3 4
| template<typename T = int> // thanks to Xeo for suggesting the default parameter
void SetItem(Key&& key, const Value& value) {
static_assert(sizeof(T) == 0,"SetItem cannot be used with temporary values as keys");
} |
这比在编译时不定义提供给定错误消息的函数有优势,而不是得到一个无效的链接器错误。
注意,static_assert必须依赖于template参数,这样它只会在使用时出错。否则,即使没有在任何地方调用编译器,编译器也可以自由编译它,并且它将一直出错。
- 我考虑过这样做,但是第二个参数不是显式的,这使得接口看起来毫无用处。这仍然是一个很好的选择,所以我投了赞成票。谢谢你的帮助。
- @我不认为这会让界面看起来毫无用处。对我来说,它表明问题出在键的类型上,而不是值的类型上。
- 我想我所说的无用的意思是,用户必须知道,即使我接受了任何东西,我也希望它不是真正的价值,如果不是,我会得到不同的疯狂编译器错误。
- 您也可以使用默认的模板参数iirc来执行此操作。
- @Xeo,这是更好的方法,并且满足了OP的问题,非常感谢。
- @安兹先生,看看现在Xeo的建议是否更好
- 这里是一个测试用例,注释掉函数调用,它编译得很好。
- 你现在有了最好的答案。不幸的是,MSVC不支持函数的默认模板参数,因此我无法使其正常工作。尽管如此,我并没有把我的问题限制在MSVC上,而且你的回答在产生有意义的错误上绝对是优雅的。我会记住这一点,因为当MS决定C++仍然是重要的。
- @mranz-wow,这对vs来说太蠢了,它只支持类模板上的默认模板参数,而不支持函数模板。请考虑重新接受Xeo的答案,因为它是您唯一可以使用的答案。我将把这个答案留在这里,以便将来的读者可以在他们的编译器支持的情况下使用它。另外,由于Xeo的建议,这只是一个可以接受的答案。
- 塞思:函数模板的默认模板参数是C++ 11的特性;VC++ 2010早于C++ 11的标准化,因此批评它没有实现它有点不公平。;
- @Ildjarn VC11也不支持它。所以我们可以严厉对待。
- @Mranz:VC11预览版不支持,但不太确定。
- @Ildjarn基本上确认了预览中的内容就是将要发布的内容。更好的C++ 11支持可以在SP1中显示出来。赫伯·萨特证实了这一点。
- 没有sizeof值是0,因此编译器仍然可以给出错误,即使不使用它(或者给出警告"静态强制转换条件将始终计算为false")。最好使用像always_false::value这样的东西。
- 无论如何,我想你可以在MSVC上使用template void SetItem(Key&& key, const Value& value) { static_assert(sizeof...(T) != 0,"..."); }。T默认为空包。
- @约翰内斯:MSVC10和11中没有变量模板(还没有)。:(
- @实际上,我很确定在模板被实例化之前,编译器是不允许这样做的。我见过很多地方,比如这里,这里,这里,这里,还有这里,还有很多聪明人的评论,等等,我都找不到。
- @赛斯,你很确定,但我完全确定,即使模板没有被实例化,它也可以被拒绝。那些地方都坏了,对不起。