2013-10-24 53 views
5

排序這是有點一個謎,而不是現實世界的問題,但我得到了到哪裏我希望能夠寫一些東西,行爲酷似陣列的constexpr初始化內容

template<int N> 
struct SortMyElements { 
    int data[N]; 

    template<typename... TT> 
    SortMyElements(TT... tt) : data{ tt... } 
    { 
     std::sort(data, data+N); 
    } 
}; 

int main() { 
    SortMyElements<5> se(1,4,2,5,3); 
    int se_reference[5] = {1,2,3,4,5}; 
    assert(memcmp(se.data, se_reference, sizeof se.data) == 0); 
} 
的情況

除了我想讓SortMyElements構造函數爲constexpr

顯然,這是可能的固定N;例如,我可以專注

template<> 
struct SortMyElements<1> { 
    int data[1]; 
    constexpr SortMyElements(int x) : data{ x } {} 
}; 


template<> 
struct SortMyElements<2> { 
    int data[2]; 
    constexpr SortMyElements(int x, int y) : data{ x>y?y:x, x>y?x:y } {} 
}; 

但是我怎麼概括到的東西,會爲任何N工作呢?


請注意,陣列元件必須來自的參數,而不是從模板非類型參數的實際值;我的元素來自constexpr表達的是,儘管在編譯時進行評估,堅決駐留在「價值體系」內,而不是「類型系統」。 (例如,Boost.MPL's sort嚴格在「類型系統」中工作。)

我發佈了一個工作「答案」,但效率太低,無法用於N > 6。我想在2 < N < 50左右使用此功能。 (PS-實際上我真正想做的是將數組中的所有零置換到數組的末尾,並將非零值向前排列,這可能比全排序更容易;但是,我的數字排序更容易描述,隨意解決「洗牌零」問題,而不是排序。)

+0

你真的可以從東西是一個'constexpr'(如您的構造函數)調用非'constexpr'功能(如排序)?能夠做到這一點真的沒有意義。 – masaers

+0

@masaers很明顯'std :: sort'不是constexpr;難題是寫一些行爲像'std :: sort'但是*是* constexpr的東西。 – Quuxplusone

+0

我明白了,對不起。編譯時排序元函數將非常酷...... – masaers

回答

2

嗯,我得到了我的低效版本進行編譯,至少在OSX上使用Clang。這是代碼。

然而,儘管它的還算快五行,我的筆記本電腦需要0.5秒六個要素7秒排序,以七個要素進行排序。 (災難性變化的表現,也取決於是否項目幾乎排序或反向排序)。我甚至沒有嘗試計時八強。顯然,這並不能擴展到我想用它做的事情。 (我會說50元是一個合理的上限爲我做作的用例,但6是不合理的渺小。)

#include <cstring> 
#include <cassert> 

template<int...> 
struct IntHolder {}; 

// Now let's make a consecutive range of ints from [A to B). 
template<int A, int B, int... Accum> 
struct IntRange_ : IntRange_<A+1, B, Accum..., A> {}; 

template<int A, int... Accum> 
struct IntRange_<A, A, Accum...> { 
    using type = IntHolder<Accum...>; 
}; 

template<int A, int B> 
using IntRange = typename IntRange_<A,B>::type; 

// And a helper function to do what std::min should be doing for us. 
template<typename... TT> constexpr int min(TT...); 
constexpr int min(int i) { return i; } 
template<typename... TT> constexpr int min(int i, TT... tt) { return i < min(tt...) ? i : min(tt...); } 

template<int N> 
struct SortMyElements { 
    int data[N]; 

    template<int... II, typename... TT> 
    constexpr SortMyElements(IntHolder<II...> ii, int minval, int a, TT... tt) : data{ 
     (a==minval ? a : SortMyElements<N>(ii, minval, tt..., a).data[0]), 
     (a==minval ? SortMyElements<N-1>(tt...).data[II] : SortMyElements<N>(ii, minval, tt..., a).data[II+1])... 
    } {} 

    template<typename... TT> 
    constexpr SortMyElements(TT... tt) : SortMyElements(IntRange<0,sizeof...(tt)-1>(), min(tt...), tt...) {} 
}; 

template<> 
struct SortMyElements<1> { 
    int data[1]; 
    constexpr SortMyElements(int x) : data{ x } {} 
    constexpr SortMyElements(IntHolder<>, int minval, int x) : SortMyElements(x) {} 
}; 

static_assert(SortMyElements<5>(5,2,1,3,1).data[0] == 1, ""); 
static_assert(SortMyElements<5>(5,2,1,3,1).data[1] == 1, ""); 
static_assert(SortMyElements<5>(5,2,1,3,1).data[2] == 2, ""); 
static_assert(SortMyElements<5>(5,2,1,3,1).data[3] == 3, ""); 
static_assert(SortMyElements<5>(5,2,1,3,1).data[4] == 5, ""); 

char global_array[ SortMyElements<5>(1,4,2,5,3).data[2] ]; 
static_assert(sizeof global_array == 3, ""); 

int main() { 
    SortMyElements<5> se(1,4,2,5,3); 
    int se_reference[5] = {1,2,3,4,5}; 
    assert(memcmp(se.data, se_reference, sizeof se.data) == 0); 
} 

更新:我還沒有想出怎麼做快速歸併排序(雖然看起來DyP's answer潛在可行的我)。但是,今天早上我確實解決了我原來的難題 - 將數字零置換到數組的末尾!我使用了遞歸分區和合並算法;代碼如下this.

8

這是醜陋的,而且很可能不是一個常量表達式進行排序(因爲需要實例化深度的)最好的方式..但瞧,合併排序:

助手類型,可回收陣列型與constexpr元素訪問:

#include <cstddef> 
#include <iterator> 
#include <type_traits> 

template<class T, std::size_t N> 
struct c_array 
{ 
    T arr[N]; 

    constexpr T const& operator[](std::size_t p) const 
    { return arr[p]; } 

    constexpr T const* begin() const 
    { return arr+0; } 
    constexpr T const* end() const 
    { return arr+N; } 
}; 

template<class T> 
struct c_array<T, 0> {}; 

append函數,該函數數組類型:

template<std::size_t... Is> 
struct seq {}; 

template<std::size_t N, std::size_t... Is> 
struct gen_seq : gen_seq<N-1, N-1, Is...> {}; 

template<std::size_t... Is> 
struct gen_seq<0, Is...> : seq<Is...> {}; 

template<class T, std::size_t N, class U, std::size_t... Is> 
constexpr c_array<T, N+1> append_impl(c_array<T, N> const& p, U const& e, 
             seq<Is...>) 
{ 
    return {{p[Is]..., e}}; 
} 
template<class T, std::size_t N, class U> 
constexpr c_array<T, N+1> append(c_array<T, N> const& p, U const& e) 
{ 
    return append_impl(p, e, gen_seq<N>{}); 
} 

合併排序:

template<std::size_t Res, class T, class It, std::size_t Accum, 
     class = typename std::enable_if<Res!=Accum, void>::type > 
constexpr c_array<T, Res> c_merge(It beg0, It end0, It beg1, It end1, 
            c_array<T, Accum> const& accum) 
{ 
    return 
beg0 == end0 ? c_merge<Res>(beg0 , end0, beg1+1, end1, append(accum, *beg1)) : 
beg1 == end1 ? c_merge<Res>(beg0+1, end0, beg1 , end1, append(accum, *beg0)) : 
*beg0 < *beg1 ? c_merge<Res>(beg0+1, end0, beg1 , end1, append(accum, *beg0)) 
       : c_merge<Res>(beg0 , end0, beg1+1, end1, append(accum, *beg1)); 
} 
template<std::size_t Res, class T, class It, class... Dummies> 
constexpr c_array<T, Res> c_merge(It beg0, It end0, It beg1, It end1, 
            c_array<T, Res> const& accum, Dummies...) 
{ 
    return accum; 
} 

template<class T, std::size_t L, std::size_t R> 
constexpr c_array<T, L+R> c_merge(c_array<T, L> const& l, 
            c_array<T, R> const& r) 
{ 
    return c_merge<L+R>(l.begin(), l.end(), r.begin(), r.end(), 
         c_array<T, 0>{}); 
} 


template<class T> 
using rem_ref = typename std::remove_reference<T>::type; 

template<std::size_t dist> 
struct helper 
{ 
    template < class It > 
    static constexpr auto merge_sort(It beg, It end) 
    -> c_array<rem_ref<decltype(*beg)>, dist> 
    { 
     return c_merge(helper<dist/2>::merge_sort(beg, beg+dist/2), 
         helper<dist-dist/2>::merge_sort(beg+dist/2, end)); 
    } 
}; 
template<> 
struct helper<0> 
{ 
    template < class It > 
    static constexpr auto merge_sort(It beg, It end) 
    -> c_array<rem_ref<decltype(*beg)>, 0> 
    { 
     return {}; 
    } 
}; 
template<> 
struct helper<1> 
{ 
    template < class It > 
    static constexpr auto merge_sort(It beg, It end) 
    -> c_array<rem_ref<decltype(*beg)>, 1> 
    { 
     return {*beg}; 
    } 
}; 

template < std::size_t dist, class It > 
constexpr auto merge_sort(It beg, It end) 
-> c_array<rem_ref<decltype(*beg)>, dist> 
{ 
    return helper<dist>::merge_sort(beg, end); 
} 

助手使用示例:

template<class T, std::size_t N> 
constexpr std::size_t array_size(T (&arr)[N]) { return N; } 

template<class T, std::size_t N> 
constexpr T* c_begin(T (&arr)[N]) { return arr; } 

template<class T, std::size_t N> 
constexpr T* c_end(T (&arr)[N]) { return arr+N; } 

用例:

constexpr int unsorted[] = {5,7,3,4,1,8,2,9,0,6,10}; // odd number of elements 
constexpr auto sorted = merge_sort<array_size(unsorted)>(c_begin(unsorted), 
                 c_end(unsorted)); 

#include <iostream> 
int main() 
{ 
    std::cout << "unsorted: "; 
    for(auto const& e : unsorted) std::cout << e << ", "; 
    std::cout << '\n'; 

    std::cout << "sorted: "; 
    for(auto const& e : sorted) std::cout << e << ", "; 
    std::cout << '\n'; 
} 

輸出:

unsorted: 5, 7, 3, 4, 1, 8, 2, 9, 0, 6, 10, 
sorted: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+0

它與鏗鏘++ 3.4中繼編譯193040調試+斷言構建相當快,即使有大約50個元素。 g ++ 4.8.1目前拒絕它,我會試着弄清楚爲什麼(想象診斷信息..) – dyp

+0

g ++ 4.8.1:* error:'(const int *)(&const int [1] {5})'不是一個常量表達式*嗯它不是一個*地址常量表達式* ...我可以通過索引來代替:( – dyp

+0

你能解釋遞歸繼承模板是如何工作的嗎 – user877329

4

我知道這是一個老問題,但我們有C++ 14(和C++ 17快)並且由於C++ 14 constexpr規則並沒有如此受限制,並且肯定有幾個人會在google中找到你的問題,因此從C++ 14開始可以完成quicksort(當然還有其他算法)。 (大學分@dyp爲constexpr陣列)

#include <utility> 
#include <cstdlib> 

template<class T> 
constexpr void swap(T& l, T& r) 
{ 
    T tmp = std::move(l); 
    l = std::move(r); 
    r = std::move(tmp); 
} 

template <typename T, size_t N> 
struct array 
{ 
    constexpr T& operator[](size_t i) 
    { 
     return arr[i]; 
    } 

    constexpr const T& operator[](size_t i) const 
    { 
     return arr[i]; 
    } 

    constexpr const T* begin() const 
    { 
     return arr; 
    } 
    constexpr const T* end() const 
    { 
     return arr + N; 
    } 

    T arr[N]; 
}; 

template <typename T, size_t N> 
constexpr void sort_impl(array<T, N> &array, size_t left, size_t right) 
{ 
    if (left < right) 
    { 
     size_t m = left; 

     for (size_t i = left + 1; i<right; i++) 
      if (array[i]<array[left]) 
       swap(array[++m], array[i]); 

     swap(array[left], array[m]); 

     sort_impl(array, left, m); 
     sort_impl(array, m + 1, right); 
    } 
} 

template <typename T, size_t N> 
constexpr array<T, N> sort(array<T, N> array) 
{ 
    auto sorted = array; 
    sort_impl(sorted, 0, N); 
    return sorted; 
} 

constexpr array<int, 11> unsorted{5,7,3,4,1,8,2,9,0,6,10}; // odd number of elements 
constexpr auto sorted = sort(unsorted); 

#include <iostream> 
int main() 
{ 
    std::cout << "unsorted: "; 
    for(auto const& e : unsorted) 
     std::cout << e << ", "; 
    std::cout << '\n'; 

    std::cout << "sorted: "; 
    for(auto const& e : sorted) 
     std::cout << e << ", "; 
    std::cout << '\n'; 
} 

LIVE DEMO