Ich habe die folgende (relativ) einfache Implementierung einer std::tuple
zip-Funktion (analog Python zip
) mit perfekter Weiterleitung:Clang Fehler "versucht, ein Referenzelement in einem Tupel mit einem rvalue" nicht durch gcc
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)...);
}
Dies scheint für zwei oder mehr Tupeln zu funktionieren, auch wenn lvalues und rvalues Mischen und selbst wenn ich eine BlabberMouth
Klasse verwenden für falsche Kopien und bewegt sich zu überprüfen:
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)
));
}
Es funktioniert auch gut wann Ich gebe es nur eine tuple
ohne Mischen lvalues und rvalues (Klirren-3.9, frühere Versionen von Klirren Drossel auf diese auch):
f(tuple_zip(forward_as_tuple(Blabbermouth("world"), 3))); // prints "world"
Allerdings, wenn ich mischen lvalues und rvalues und nur ein Tupel geben, clang
flippt aus über etwas in einer noexecpt
Spezifikation (aber gcc ist in Ordnung, und sogar richtig läuft):
auto x = BlabberMouth("hello");
f(tuple_zip(forward_as_tuple(x, 3))); // clang freaks out, gcc okay
Was (wenn überhaupt) mache ich falsch? Sollte gcc sich beschweren, oder sollte sich klingeln nicht beschweren? Hat mein Code irgendwelche falschen Referenzen, mit denen ich gerade "Glück habe", und deshalb protestiert clang? Hätte ich das anders machen sollen? Wenn der Klang hier falsch ist, kann jemand einen Workaround vorschlagen? (Und/oder verknüpfen Sie mich an einen Fehlerbericht?)
aktualisieren
@Oktalist eine viel minimales Beispiel dazu beigetragen, dass das gleiche Problem zeigt:
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());
}
(ich gedacht hatte machen mein Beispiel auch minimal, aber ich war mir nicht sicher, ob das, was ich tat, genau analog war, hauptsächlich weil ich nicht ganz verstehe, wie perfektes Forwarding mit auto
/decltype(auto)
Rückgabewerten, Rückgabewertoptimierung (RVO) interagiert. std::get
und std::make_tuple
, so wollte ich sicher sein, ich nicht sonst dumm, etwas zu tun war)
[Minimal Beispiel mit dem gleichen Problem] (https://godbolt.org/g/3ZzuJ6) – Oktalist
Klingt wie https://llvm.org/bugs/show_bug.cgi?id=22806, im letzten Monat behoben. @Oktalists minimiertes Beispiel funktioniert bei trunk. –
@ T.C.Das ist großartig, aber es ist für uns im Grunde unmöglich, von unseren Benutzern zu verlangen, dass sie den Klangstamm benutzen. Können Sie eine Problemumgehung vorschlagen, die keine falschen Kopien generiert? –