【发布时间】:2021-11-22 04:53:24
【问题描述】:
我正在试验一个计算余弦相似度的简单代码:
#include <iostream>
#include <numeric>
#include <array>
#include <cmath>
float safe_divide(const float& a, const float& b) { return b < 1e-8f && b > -1e-8f ? 0.f : a / b; }
template< size_t N >
float cosine_similarity( std::array<float, N> a, std::array<float, N> b )
{
const float&& a2 = std::move( std::inner_product( a.begin(), a.end(), a.begin(), 0.f ) );
const float&& b2 = std::move( std::inner_product( b.begin(), b.end(), b.begin(), 0.f ) );
const float&& dot_product = std::move( std::inner_product( a.begin(), a.end(), b.begin(), 0.f ) );
return safe_divide( dot_product, ( std::sqrt(a2) * std::sqrt(b2) ) );
}
int main(){
std::array<float, 5> a{1,1,1,1,1}, b{-1,1,-1,1,-1};
std::cout<<cosine_similarity(a,b);
}
在 x86-64 Clang 12.0.1(和其他版本)上,它可以编译并给出正确的答案。
然而,在我测试过的每个版本的 GCC 上,它都能编译,但给出了错误的答案(或没有答案)。
它提出了几个问题:
- 我对@987654323@ 的使用是否有效?
- 为什么似乎只有 Clang 可以使用这个编译器而没有其他编译器?
- 标准是怎么说的?
这里是实验的链接:https://godbolt.org/z/KWbMYorrc
【问题讨论】:
-
这里为什么需要
std::move?那些是r-values。 -
这是一个了解 r 值和
std::move的有趣问题,但正如 Marek 所说,您的代码中不需要任何这些。无论如何,移动float只是一个副本 -
std::inner_product is returning an l-valueSoo,你觉得你可以std::inner_product(...) = something;吗? -
任何函数的返回值都不需要
std::move(即使它返回移动引用)。 -
按照您编写它的方式,编译器创建了
float类型的临时对象,并且您对它的引用时间长于导致 UB 的生命周期。