我寫了std::tuple
zip函數(類似於Python的zip
)的以下(相對)簡單的實現:Clang錯誤「試圖構造一個具有右值的元組中的引用元素」不是由gcc提供的
template <typename Tuple>
void f(Tuple&& tup) {
cout << get<0>(get<0>(tup)).data << endl;
}
struct Blabbermouth {
Blabbermouth(string const& str) : data(str) { }
Blabbermouth(Blabbermouth const& other) : data(other.data) { cout << data << " copied" << endl; }
Blabbermouth(Blabbermouth&& other) : data(move(other.data)) { cout << data << " moved" << endl; }
string data;
};
int main(int argc, char** argv) {
Blabbermouth x("hello ");
// prints "hello"
f(tuple_zip(
forward_as_tuple(x, 2),
forward_as_tuple(Blabbermouth("world"), 3)
));
}
它也能正常工作:
template <size_t I, size_t N>
struct tuple_zip_helper {
template <typename... Tuples>
constexpr auto operator()(Tuples&&... tuples) const {
return tuple_cat(
make_tuple(forward_as_tuple(get<I>(forward<Tuples>(tuples))...)),
tuple_zip_helper<I+1, N>()(forward<Tuples>(tuples)...)
);
}
};
template <size_t N>
struct tuple_zip_helper<N, N> {
template <typename... Tuples>
constexpr auto operator()(Tuples&&...) const {
return forward_as_tuple();
}
};
namespace std {
// Extend min to handle single argument case, for generality
template <typename T>
constexpr decltype(auto) min(T&& val) { return forward<T>(val); }
}
template <typename... Tuples>
auto tuple_zip(Tuples&&... tuples) {
static constexpr size_t min_size = min(tuple_size<decay_t<Tuples>>::value...);
return tuple_zip_helper<0, min_size>()(forward<Tuples>(tuples)...);
}
這種混合左值和右值,甚至當我使用BlabberMouth
類檢查假拷貝和移動時顯得做工精細兩個或兩個以上的元組,甚至什麼時候 我給它只是一個tuple
沒有混合左值和右值(鐺-3.9,在這個早期版本鐺扼流圈的爲好):
f(tuple_zip(forward_as_tuple(Blabbermouth("world"), 3))); // prints "world"
然而,當我混合左值和右值和只給一個元組, clang
怪胎約在noexecpt
規範的東西(但GCC是好的,甚至可以正常運行):
auto x = BlabberMouth("hello");
f(tuple_zip(forward_as_tuple(x, 3))); // clang freaks out, gcc okay
我做錯什麼(如果有的話)? gcc應該抱怨,還是應該不要抱怨?我的代碼是否有任何懸而未決的引用,我只是「幸運」,這就是爲什麼clang會反對?我應該以不同的方式做到嗎?如果鏗鏘是一個錯誤在這裏,任何人都可以提出一個解決方法? (和/或鏈接我的錯誤報告?)
更新
@Oktalist貢獻了一個更小例子,說明了同樣的問題:
struct foo {};
int main(int argc, char** argv)
{
foo f;
std::tuple<foo&> t(f);
std::tuple_cat(std::make_tuple(t), std::make_tuple());
}
(我曾考慮製作我的例子也是最小的,但我不確定我在做什麼與此完全類似,主要是因爲我不完全理解完美轉發與返回值,返回值優化(RVO), std::get
和std::make_tuple
,所以我想確保我沒有做什麼別的笨)
[展示相同問題的最小示例](https://godbolt.org/g/3ZzUJ6) – Oktalist
聽起來像https://llvm.org/bugs/show_bug.cgi?id=22806,上個月已修復。 @Oktalist的最小化示例適用於主幹。 –
@ T.C。這很好,但我們基本上不可能要求我們的用戶使用叮噹中繼線。你能建議一種不會產生虛假副本的解決方法嗎? –