C++不同的minmax实现

bar*_*top 9 c++ stl c++11

因为你可能(不)知道使用std::minmax自动和临时参数可能是危险的.以下代码例如是UB,因为std::minmax返回引用对,而不是值:

auto fun(){
    auto res = std::minmax(3, 4);
    return res.first;
}    
Run Code Online (Sandbox Code Playgroud)

我想问一下是否有可能使std::minmax功能安全或至少更安全,没有任何开销?我提出了这样的解决方案,但我不完全确定它是否等同于当前,minmax因为生成的程序集对于类似stl的实现和我的不同.所以,问题是:什么是我实现的可能的问题/缺点,minmax相对于std样之一:

//below is std-like minmax
template< class T > 
constexpr std::pair<const T&,const T&> std_minmax( const T& a, const T& b ){
    return (b < a) ? std::pair<const T&, const T&>(b, a)
            : std::pair<const T&, const T&>(a, b);
}

//below is my minmax implementation
template< class T > 
constexpr std::pair<T, T> my_minmax( T&& a, T&& b ){
    return (b < a) ? std::pair<T, T>(std::forward<T>(b), std::forward<T>(a))
            : std::pair<T, T>(std::forward<T>(a), std::forward<T>(b));
}
Run Code Online (Sandbox Code Playgroud)

在godbolt.org上进行现场演示


正如你们中的一些人声称不清楚我在问什么,我想重新说一下我想要的东西.我想编写与其完全相同的函数std::minmax,但如果给出一个临时值 - 则返回std::pair<T, T>而不是std::pair<const T &, const T &>.其次,在执行此操作时,我希望避免任何不必要的移动,复制数据等.

Dan*_*ica 3

我不太确定你想实现什么目标。你写了:

没有任何开销

但您的解决方案将复制左值参数。是你想要的吗?

无论如何,您不能以这种方式使用具有相同模板参数的两个转发引用,因为如果两个函数参数具有不同的类别,它将失败:

template <typename T> void f(T&& a, T&& b) { }

int main() {
  int a = 3;
  f(a, 1);  // error: template argument deduction/substitution failed
}
Run Code Online (Sandbox Code Playgroud)

对于第一个函数参数,T将被推导为int&,对于第二个函数参数,将被推导为int


如果您想删除任何复制,唯一的可能是结果的成员pair是:

  1. 对相应函数参数的(const)左值引用(如果它是左值)

  2. 如果它是右值则从该参数移动的值​​。

我认为这是不可能实现的。考虑:

std::string a("hello");
auto p = minmax(a, std::string("world"));
Run Code Online (Sandbox Code Playgroud)

这里的结果类型是std::pair<std::string&, std::string>. 但是,如果

auto p = minmax(a, std::string("earth"));
Run Code Online (Sandbox Code Playgroud)

结果类型会有所不同,即std::pair<std::string, std::string&>.

因此,结果类型将取决于运行时条件(通常需要运行时多态性)。


更新

出于好奇,我刚刚想出了一个包装器,它可以通过(常量)指针来保存某些对象:

template <typename T>
class val_or_ptr {
   std::variant<T, const T*> v_;
public:
   val_or_ptr(const T& arg) : v_(&arg) { }
   val_or_ptr(T&& arg) : v_(std::move(arg)) { }
   const T& get() const { return v_.index() ? *std::get<const T*>(v_) : std::get<T>(v_); }
};
Run Code Online (Sandbox Code Playgroud)

这样,您可以定义minmax为:

template <typename T, typename U,
          typename V = std::enable_if_t<std::is_same_v<std::decay_t<T>, std::decay_t<U>>, std::decay_t<T>>>
std::pair<val_or_ptr<V>, val_or_ptr<V>> minmax(T&& a, U&& b) {
   if (b < a) return { std::forward<U>(b), std::forward<T>(a) };
   else return { std::forward<T>(a), std::forward<U>(b) };
}
Run Code Online (Sandbox Code Playgroud)

现场演示在这里:https ://wandbox.org/permlink/N3kdI4hzllBGFWVH

这是非常基本的实现,但它应该防止从 的左值和右值参数进行复制minmax