拥有一个函数只接受非常量左值
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: In function 'int main()': prog.cpp:9:45: error: invalid
initialization of non-const reference of type 'std::vector&' from
an rvalue of type 'std::vector' sort(vector{2,1,3},
vector{3,1,2});
^ prog.cpp:6:6: note: initializing argument 1 of 'void sort(A&, B&) [with A =
std::vector; 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 considered harmful", Microsoft senior engineer Stephan T. Lavavej even recommends not using the flag, but you should also have a look at the comments at STL Fixes In VS 2015, Part 2 的帖子中,他说:
We've definitely had meetings about the /Za
and /Zc
conformance
options. We ultimately want to get to a point where VC is conformant
by default, without having to request extra options, so that becomes
the most-used and most-tested path. As you can see in the post, I've
been working towards this in the STL by removing non-Standard
machinery whenever possible.
因此,在某些未来版本的 MSVC 中,默认情况下这可能是一个编译错误。
还有一点:C++标准不区分错误和警告,只讲"diagnostic messages"。这意味着 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 是否正确地选择了完美转发的那个,但我希望如此。
您可以明确 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;
}
作为您尝试解决的 X 问题的答案,而不是您提出的 Y 问题的答案...正确的答案是您不应该做您正在尝试做的事情。无法想象某事如何有用并不足以成为阻止人们使用它的充分理由。
而且,事实上,我什至不必抽象地提出这一点:这里有两个具体示例,其中接受临时对象会很有用。
您可能只关心以下两个对象之一:
interesting_container A;
// fill A
sort(an_ordering_criterion(), A);
容器不是 'self-contained';例如提供另一个容器视图的容器:
vector<int> A, B;
// fill A and B
sort(make_subsequence(A, 1, 10), make_subsequence(B, 5, 14));
我有一个函数可以对两个向量进行排序,其中第一个向量作为排序标准。它的签名是
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: In function 'int main()': prog.cpp:9:45: error: invalid initialization of non-const reference of type 'std::vector&' from an rvalue of type 'std::vector' sort(vector{2,1,3}, vector{3,1,2}); ^ prog.cpp:6:6: note: initializing argument 1 of 'void sort(A&, B&) [with A = std::vector; 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 considered harmful", Microsoft senior engineer Stephan T. Lavavej even recommends not using the flag, but you should also have a look at the comments at STL Fixes In VS 2015, Part 2 的帖子中,他说:
We've definitely had meetings about the
/Za
and/Zc
conformance options. We ultimately want to get to a point where VC is conformant by default, without having to request extra options, so that becomes the most-used and most-tested path. As you can see in the post, I've been working towards this in the STL by removing non-Standard machinery whenever possible.
因此,在某些未来版本的 MSVC 中,默认情况下这可能是一个编译错误。
还有一点:C++标准不区分错误和警告,只讲"diagnostic messages"。这意味着 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 是否正确地选择了完美转发的那个,但我希望如此。
您可以明确 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;
}
作为您尝试解决的 X 问题的答案,而不是您提出的 Y 问题的答案...正确的答案是您不应该做您正在尝试做的事情。无法想象某事如何有用并不足以成为阻止人们使用它的充分理由。
而且,事实上,我什至不必抽象地提出这一点:这里有两个具体示例,其中接受临时对象会很有用。
您可能只关心以下两个对象之一:
interesting_container A;
// fill A
sort(an_ordering_criterion(), A);
容器不是 'self-contained';例如提供另一个容器视图的容器:
vector<int> A, B;
// fill A and B
sort(make_subsequence(A, 1, 10), make_subsequence(B, 5, 14));