2016-05-13 31 views
0

我寫了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 

Live Demo

我做錯什麼(如果有的話)? 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::getstd::make_tuple,所以我想確保我沒有做什麼別的

+3

[展示相同問題的最小示例](https://godbolt.org/g/3ZzUJ6) – Oktalist

+3

聽起來像https://llvm.org/bugs/show_bug.cgi?id=22806,上個月已修復。 @Oktalist的最小化示例適用於主幹。 –

+0

@ T.C。這很好,但我們基本上不可能要求我們的用戶使用叮噹中繼線。你能建議一種不會產生虛假副本的解決方法嗎? –

回答

1

該錯誤是由調用引起tuple_cat(雖然它不完全。它似乎在libC++的tuple的構造函數的拜占庭式迷宮和SFINAE條件中有些混亂),所以解決方法是避免使用它。

反正做遞歸tuple_cat沒有意義;一包擴展(或兩個)會做。

template<size_t I, typename... Tuples> 
constexpr auto tuple_zip_one(Tuples&&... tuples) { 
    return forward_as_tuple(get<I>(forward<Tuples>(tuples))...); 
} 

template<size_t...Is, typename... Tuples> 
constexpr auto tuple_zip_helper(index_sequence<Is...>, Tuples&&... tuples) { 
    return make_tuple(tuple_zip_one<Is>(forward<Tuples>(tuples)...)...); 
} 

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(make_index_sequence<min_size>(), forward<Tuples>(tuples)...); 
} 

我把刪除你UB誘導min過載,只需使用標準initializer_list版本,而不是自由。

Demo

相關問題