2013-10-24 51 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