让函数只接受非const左值
Having a function only accept non-const lvalues
我有一个函数,它以第一个向量作为排序标准对两个向量进行排序。它的签名是
template<typename A, typename B>
void sort(A&& X, B&& Y)
{
..
}
问题是通用引用会允许无意义的情况,如
sort(vector<int>{ 2,1,3 }, vector<int>{ 3,1,2 });
右值之后将被销毁(无意义)。
自
起显式请求左值不起作用template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
sort(vector<int>{2,1,3}, vector<int>{3,1,2});
由于某种原因编译(我认为只有const左值被允许绑定到右值并延长其生命周期?)。
如果我将const
添加到左值引用中,则函数将不再能够修改向量并对它们进行排序。
我的问题是:
1)为什么在标记为// (*)
的例子中,我可以将右值绑定到甚至不是const
的左值?为什么像int& r = 20;
这样的东西是不允许的?有什么区别?
2)我如何解决我的问题,即让函数只接受左值而不是右值临时值?(当然,如果可能的话)
显然我可以使用任何可用的c++版本
答案是:你的编译器错误。
检查gcc或clang或类似的,你会得到这样的内容:
prog.cpp:在函数'int main()'中:prog.cpp:9:45:错误:无效从'std::vector&'类型的非const引用初始化类型为"std::vector"的右值sort(vector{2,1,3});向量{3 1 2});^ prog.cpp:6:6:注意:初始化'void sort(A&, B&) [with A =std::向量;B = std::vector]' void sort(A&X, B&Y) {}
您可以使用/Za
编译器选项将此转换为错误:
error C2664: 'void sort<std::vector<int,std::allocator<_Ty>>,std::vector<_Ty,std::allocator<_Ty>>>(A &,B &)' : cannot convert argument 1
from 'std::vector<int,std::allocator<_Ty>>' to 'std::vector<int,std::allocator<_Ty>> &'
with
[
_Ty=int
, A=std::vector<int,std::allocator<int>>
, B=std::vector<int,std::allocator<int>>
]
and
[
_Ty=int
]
and
[
_Ty=int
]
A non-const reference may only be bound to an lvalue
请注意,/Za
在过去有相当多的问题,甚至现在仍然破坏<windows.h>
,所以你不能在所有的编译单元中使用它。在2012年的一篇题为"MSVC/Za被认为是有害的"的帖子中,微软高级工程师Stephan T. Lavavej甚至建议不要使用该标志,但你也应该看看VS 2015中STL修复的评论,第2部分,他说:
我们确实有关于
/Za
和/Zc
一致性的会议选项。我们最终想要达到VC是一致的默认情况下,不需要请求额外的选项,所以就变成最常用和测试最多的路径。正如你在帖子里看到的,我一直在STL中通过删除非标准来实现这一点
因此,在MSVC的某些未来版本中,默认情况下这将是一个编译错误。
还有一件事:c++标准不区分错误和警告,它只讨论"诊断消息"。这意味着MSVC实际上是符合只要它产生一个警告。
正如其他答案所指出的,编译器是错误的。
无需更改编译器的编译器选项:
struct sfinae_helper {};
template<bool b>
using sfinae = typename std::enable_if<b, sfinae_helper>::type*;
// sfinae_helper, because standard is dumb: void*s are illegal here
template<class A, class B,
sfinae<!std::is_const<A>::value&&!std::is_const<B>::value> = nullptr
>
void sort(A& X, B& Y) ... // (*)
sort(vector<int>{2,1,3}, vector<int>{3,1,2});
在MSVC2013中也将无法编译,在兼容的编译器中应该是兼容的。
请注意,虽然将A
和B
演绎为const X
在标准下是不合法的,但将const X
显式地传递为A
或B
是。
最后一种方法是:
template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
template<typename A, typename B>
void sort(A&& X, B&& Y) = delete;
,我们生成一个显式删除的,应该优先于A&, B&
的。我不知道MSVC在这种情况下是否正确地选择了完美转发的一个,但我希望如此。
作为你试图解决的X问题的答案,而不是你问的Y问题的答案…正确的答案是,你不应该做你想做的事。不能想象某件事如何有用,并不能成为你千方脑汁阻止别人做这件事的充分理由。
事实上,我甚至不需要抽象地提出这一点:这里有两个具体的例子,其中接受临时对象将是有用的。
你可能只关心两个对象中的一个:
interesting_container A;
// fill A
sort(an_ordering_criterion(), A);
容器不是"自包含的";例如,一个容器提供了另一个容器的视图:
vector<int> A, B;
// fill A and B
sort(make_subsequence(A, 1, 10), make_subsequence(B, 5, 14));
您可以显式地delete
不希望的sort
函数重载:
#include <iostream>
#include <vector>
#include <cstdlib>
template< typename X, typename Y >
void
sort(X &, Y &)
{
static_assert(!std::is_const< X >{});
static_assert(!std::is_const< Y >{});
}
template< typename X, typename Y >
int
sort(X const &, Y &) = delete;
template< typename X, typename Y >
int
sort(X &, Y const &) = delete;
template< typename X, typename Y >
int
sort(X const &, Y const &) = delete;
int
main()
{
std::vector< int > v{1, 3, 5};
std::vector< int > const c{2, 4, 6};
::sort(v, v); // valid
{ // has been explicitly deleted
//::sort(v, c);
//::sort(c, v);
//::sort(c, c);
}
{ // not viable: expects an l-value for 1st argument
//::sort(std::move(v), v);
//::sort(std::move(v), c);
//::sort(std::move(c), v);
//::sort(std::move(c), c);
}
{ // not viable: expects an l-value for 2nd argument
//::sort(v, std::move(v));
//::sort(v, std::move(c));
//::sort(c, std::move(v));
//::sort(c, std::move(c));
}
{ // not viable: expects an l-value for 1st or 2nd argument
//::sort(std::move(v), std::move(v));
//::sort(std::move(v), std::move(c));
//::sort(std::move(c), std::move(v));
//::sort(std::move(c), std::move(c));
}
return EXIT_SUCCESS;
}
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 无法使用类型 'const char *' 的左值初始化类型 'char *' 的成员子对象
- 将返回类型专用化为 void 或 const 左值引用
- 为什么 const 函数返回左值而不是右值?
- 为什么我需要将默认引用参数定义为 const 以便为其分配一个左值?
- 为什么静态常量字符 * const 变量在为左值时可绑定到右值引用参数
- 无法使用类型 'const char [X]' 的左值初始化类型 'const signed char *' 的成员子对象
- 为什么不能将"const char*"的左值绑定到"const char* const&&?
- C++11:对const、volatile、左值引用和右值引用限定的成员函数指针进行抽象
- Memory_order在由非const左值传递时变为default
- 将boost::python::object转换为(非const)左值
- 让函数只接受非const左值
- 在c++ 11中将非const左值引用绑定到右值是否有效?(修改)
- 在初始化表达式中强制左值的const-ness
- 右值或左值(const)引用形参
- 是否需要重载接受const左值引用的方法来显式地进行右值引用?
- 错误:非const引用的初始值必须为左值
- 操作符+()选择右值引用变量而不是const左值变量
- 防止将左值引用绑定到非const对象
- 非const引用只能绑定到左值