returns 个可修改引用的 minmax
minmax that returns modifiable references
有了 C++ 的所有新功能(我认为 C++11 就足够了),是什么阻止了 std::minmax
函数 returns 一对引用。
这样一来,如果一个人提供两个可修改的引用,就可以修改它们。这是在开罐头吗?
#include<functional>
// maybe all this options can be simplified
template<class T1, class T2> struct common;
template<class T> struct common<T, T>{using type = T;};
template<class T> struct common<T const&, T&>{using type = T const&;};
template<class T> struct common<T&, T const&>{using type = T const&;};
template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};
template<class T1, class T2, class Compare = std::less<>, class Ret = typename common<T1, T2>::type>
std::pair<Ret, Ret> minmax(T1&& a, T2&& b, Compare comp = {}){
return comp(b, a) ?
std::pair<Ret, Ret>(std::forward<T2>(b), std::forward<T1>(a))
: std::pair<Ret, Ret>(std::forward<T1>(a), std::forward<T2>(b));
}
测试:
#include<cassert>
int main(){
{
int a = 1;
int b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
small += 1;
assert(a == 2);
}{
int const a = 1;
int b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int a = 1;
int const b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int const a = 1;
int const b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int b = 10;
auto& small = minmax(int(1), b).first;
assert(small == 1);
// small += 1; error small is const reference, because first argument was const
}{
int a = 1;
auto& small = minmax(a, int(10)).first;
assert(small == 1);
// small += 1; error small is const reference, because second argument was const
}
{
int const a = 1;
auto& small = minmax(a, int(10)).first;
assert(small == 1);
// small += 1; error small is const reference, because both arguments are const
}
{
// auto& small = minmax(int(1), int(10)).first; // error, not clear why
auto const& small = minmax(int(1), int(10)).first; // ok
// auto small2 = minmax(int(1), int(10)).first; // also ok
assert(small == 1);
// small += 1; error small is const reference, because both arguments are const
}
}
很久以前,Howard Hinnant: N2199 就有一篇类似这样的论文。它非常开放的示例演示了您要解决的确切问题:
The function can not be used on the left hand side of an assignment:
int x = 1;
int y = 2;
std::min(x, y) = 3; // x == 3 desired, currently compile time error
它接着列举了经常出现的悬空引用问题、混合类型以及对仅移动类型有用的例子,并继续提出 min
和 max
的新版本解决所有这些问题 - 它在底部包含一个非常彻底的实现(太长无法粘贴到此处)。基于此实施 minmax()
应该非常简单:
template <class T, class U,
class R = typename min_max_return<T&&, U&&>::type>
inline
std::pair<R, R>
minmax(T&& a, U&& b)
{
if (b < a)
return {std::forward<U>(b), std::forward<T>(a)};
return {std::forward<T>(a), std::forward<U>(b)};
}
论文当时被拒了。它可能会回来。
能够取回可变引用很好,但能够避免悬空引用就更好了。匿名引用最近看到的一个例子:
template<typename T> T sign(T);
template <typename T>
inline auto frob(T x, T y) -> decltype(std::max(sign(x - y), T(0))) {
return std::max(sign(x - y), T(0));
}
This function has undefined behaviour for all inputs (the narrowest
contract possible?).
请注意,您的 common
实施存在此问题。这些案例:
template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};
全挂了。这意味着如果我有:
int i = 4;
auto result = your_minmax(i, 5);
result
这里有一个 pair<int const&, int const&>
,其中一个是对 i
的引用,另一个是悬挂的。为了安全起见,所有这些情况都必须执行 using type = T;
。
有了 C++ 的所有新功能(我认为 C++11 就足够了),是什么阻止了 std::minmax
函数 returns 一对引用。
这样一来,如果一个人提供两个可修改的引用,就可以修改它们。这是在开罐头吗?
#include<functional>
// maybe all this options can be simplified
template<class T1, class T2> struct common;
template<class T> struct common<T, T>{using type = T;};
template<class T> struct common<T const&, T&>{using type = T const&;};
template<class T> struct common<T&, T const&>{using type = T const&;};
template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};
template<class T1, class T2, class Compare = std::less<>, class Ret = typename common<T1, T2>::type>
std::pair<Ret, Ret> minmax(T1&& a, T2&& b, Compare comp = {}){
return comp(b, a) ?
std::pair<Ret, Ret>(std::forward<T2>(b), std::forward<T1>(a))
: std::pair<Ret, Ret>(std::forward<T1>(a), std::forward<T2>(b));
}
测试:
#include<cassert>
int main(){
{
int a = 1;
int b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
small += 1;
assert(a == 2);
}{
int const a = 1;
int b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int a = 1;
int const b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int const a = 1;
int const b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int b = 10;
auto& small = minmax(int(1), b).first;
assert(small == 1);
// small += 1; error small is const reference, because first argument was const
}{
int a = 1;
auto& small = minmax(a, int(10)).first;
assert(small == 1);
// small += 1; error small is const reference, because second argument was const
}
{
int const a = 1;
auto& small = minmax(a, int(10)).first;
assert(small == 1);
// small += 1; error small is const reference, because both arguments are const
}
{
// auto& small = minmax(int(1), int(10)).first; // error, not clear why
auto const& small = minmax(int(1), int(10)).first; // ok
// auto small2 = minmax(int(1), int(10)).first; // also ok
assert(small == 1);
// small += 1; error small is const reference, because both arguments are const
}
}
很久以前,Howard Hinnant: N2199 就有一篇类似这样的论文。它非常开放的示例演示了您要解决的确切问题:
The function can not be used on the left hand side of an assignment:
int x = 1; int y = 2; std::min(x, y) = 3; // x == 3 desired, currently compile time error
它接着列举了经常出现的悬空引用问题、混合类型以及对仅移动类型有用的例子,并继续提出 min
和 max
的新版本解决所有这些问题 - 它在底部包含一个非常彻底的实现(太长无法粘贴到此处)。基于此实施 minmax()
应该非常简单:
template <class T, class U,
class R = typename min_max_return<T&&, U&&>::type>
inline
std::pair<R, R>
minmax(T&& a, U&& b)
{
if (b < a)
return {std::forward<U>(b), std::forward<T>(a)};
return {std::forward<T>(a), std::forward<U>(b)};
}
论文当时被拒了。它可能会回来。
能够取回可变引用很好,但能够避免悬空引用就更好了。匿名引用最近看到的一个例子:
template<typename T> T sign(T); template <typename T> inline auto frob(T x, T y) -> decltype(std::max(sign(x - y), T(0))) { return std::max(sign(x - y), T(0)); }
This function has undefined behaviour for all inputs (the narrowest contract possible?).
请注意,您的 common
实施存在此问题。这些案例:
template<class T> struct common<T, T&>{using type = T const&;}; template<class T> struct common<T&, T>{using type = T const&;}; template<class T> struct common<T const&, T>{using type = T const&;}; template<class T> struct common<T, T const&>{using type = T const&;};
全挂了。这意味着如果我有:
int i = 4;
auto result = your_minmax(i, 5);
result
这里有一个 pair<int const&, int const&>
,其中一个是对 i
的引用,另一个是悬挂的。为了安全起见,所有这些情况都必须执行 using type = T;
。