2015-09-04 5 views
5

this question का उत्तर देने और this talk पढ़ने और this code पर पढ़ने के बाद, मैं केवल सरल सरणी वर्ग के साथ constexpr find को कार्यान्वित करना चाहता हूं।कॉन्स्टेक्सप्रॉप कार्यान्वयन

उदाहरण निम्नलिखित पर विचार करें:

#include <cstddef> 

template <class It, class T> 
constexpr auto constexpr_find(const It& b, const It& e, T value) { 
    auto begin = b; 
    while (begin != e) { 
     if (*begin == value) break; 

     ++begin; 
    } 
    return *begin; 
} 

template<typename T, size_t N> 
class array 
{ 
public: 
    typedef T* iterator; 
    typedef const T* const_iterator; 
    constexpr auto begin() const { return const_iterator(array_); } 
    constexpr auto end() const { return const_iterator(array_ + N); } 

    T array_[N]; 
    static constexpr size_t size = N; 
}; 

int main() 
{ 
    constexpr array<int, 3> array{{0,2,3}}; 
    static_assert(constexpr_find(array.begin(), array.end(), 0) == 0, ""); 
} 

compiles as expected

और कस्टम constexpr इटरेटर साथ:

template<class T> 
class array_iterator 
{ 
public: 
    constexpr array_iterator(const T* v) : iterator(v) 
    { 
    } 
    constexpr const T& operator *() const { return *iterator; } 
    constexpr array_iterator& operator ++() 
    { 
     ++iterator; 
     return *this; 
    } 
    constexpr bool operator != (const array_iterator& other) const { return iterator != other.iterator; } 
private: 
    const T* iterator; 
}; 

सरणी वर्ग में:

typedef const array_iterator<const T> const_iterator; 

है कि फर्क सिर्फ इतना है, संकलक मुझे त्रुटि दे:

in constexpr expansion of constexpr_find<array_iterator<const int>, int>(array.array<T, N>::begin<int, 3u>(), array.array<T, N>::end<int, 3u>(), 0)

error: (((const int*)(& array.array<int, 3u>::array_)) != (((const int*)(& array.array<int, 3u>::array_)) + 12u)) is not a constant expression

Live example

, इस जीसीसी बग है के बाद से बजना इस ठीक संकलन, या वहाँ दो स्निपेट में अंतर है?

+0

[ओटी]: 'constexpr_find' में, आप ऐसे मामले का प्रबंधन नहीं करते हैं जहां तत्व मौजूद नहीं है क्योंकि आप इटरेटर के बजाय तत्व लौटाते हैं। – Jarod42

+0

@ जारोड 42 धन्यवाद, मुझे पता है। यह constexpr_additions प्रस्ताव से सिर्फ एक उदाहरण है। – ForEveR

+0

boost :: mpl orgasmic – Sergei

उत्तर

1

मैं निश्चित रूप से नहीं कह सकता, लेकिन आप सरणी के सदस्य के लिए बाहरी इटरेटर वर्ग में पॉइंटर्स स्टोर करते हैं, यह उस त्रुटि का कारण हो सकता है।

constexpr const struct A { int i[2]; } a {{0,0}}; 

int main() 
{ 
    static_assert (nullptr != a.i , ""); // ok 
    static_assert (nullptr != a.i+0, ""); // ok 
    static_assert (nullptr != a.i+1, ""); // error 
} 

यह निषिद्ध किया जा रहा है:

--------- अद्यतन शुरू ---------

यहाँ कम से कम टुकड़ा है कि समस्या को दर्शाता है है निरंतर अभिव्यक्तियों में सरणी तत्वों (गैर-शून्य ऑफसेट के साथ) को पॉइंटर्स रखने के लिए।

--------- अद्यतन अंत ---------

वैकल्पिक हल तुच्छ है - सरणी वस्तु और ऑफसेट करने के लिए सूचक की दुकान।

Live

#include <cstddef> 

template <class It, class T> 
constexpr auto constexpr_find(const It& b, const It& e, T value) { 
    auto begin = b, end = e; 
    while (begin != end) { 
     if (*begin == value) break; 

     ++begin; 
    } 
    return *begin; 
} 

template<class Array> 
class array_iterator 
{ 
public: 
    constexpr array_iterator(const Array& a, size_t pos=0u) : array_(&a), pos_ (pos) 
    { 
    } 
    constexpr const typename Array::value_type& 
    operator *() const { return (*array_)[pos_]; } 

    constexpr array_iterator& operator ++() 
    { 
     ++pos_; 
     return *this; 
    } 
    constexpr bool operator != (const array_iterator& other) const 
    { return array_ != other.array_ || pos_ != other.pos_; } 

private: 
    const Array* array_; 
    size_t pos_; 
}; 

template<typename T, size_t N> 
class array 
{ 
public: 
    typedef T value_type; 
    typedef const array_iterator<array> const_iterator; 
    constexpr T const& operator[] (size_t idx) const { return array_[idx]; } 
    constexpr auto begin() const { return const_iterator(*this); } 
    constexpr auto end() const { return const_iterator(*this, N); } 

    T array_[N]; 
    static constexpr size_t size = N; 
}; 

int main() 
{ 
    constexpr array<int, 3> array{{0,2,3}}; 
    static_assert(constexpr_find(array.begin(), array.end(), 0) == 0, ""); 
} 

वैसे, यह constexpr की सी ++ 11 संस्करण को लागू करना संभव है सक्षम खोज:

Live

#include <cstddef> 
#include <cassert> 

#if !defined(__clang__) && __GNUC__ < 5 
// TODO: constexpr asserts does not work in gcc4, but we may use 
// "thow" workaround from 
// http://ericniebler.com/2014/09/27/assert-and-constexpr-in-cxx11/ 
# define ce_assert(x) ((void)0) 
#else 
# define ce_assert(x) assert(x) 
#endif 
namespace my { 

template <class It, class T> 
inline constexpr It 
find (It begin, It end, T const& value) noexcept 
{ 
    return ! (begin != end && *begin != value) 
     ? begin 
     : find (begin+1, end, value); 
} 

template<class Array> 
class array_iterator 
{ 
public: 
    using value_type = typename Array::value_type; 

    constexpr array_iterator(const Array& array, size_t size = 0u) noexcept 
    : array_ (&array) 
    , pos_ (size) 
    {} 

    constexpr const value_type operator*() const noexcept 
    { 
    return ce_assert (pos_ < Array::size), (*array_) [pos_]; 
    } 

#if __cplusplus >= 201402L // C++14 
    constexpr 
#endif 
    array_iterator& operator ++() noexcept 
    { 
    return ce_assert (pos_ < Array::size), ++pos_, *this; 
    } 

    constexpr array_iterator operator+ (size_t n) const noexcept 
    { 
    return ce_assert (pos_+n <= Array::size), array_iterator (*array_, pos_+n); 
    } 

    friend constexpr bool 
    operator != (const array_iterator& i1, const array_iterator& i2) noexcept 
    { 
    return i1.array_ != i2.array_ || i1.pos_ != i2.pos_; 
    } 

    friend constexpr size_t 
    operator- (array_iterator const& i1, array_iterator const& i2) noexcept 
    { 
    return ce_assert (i1.array_ == i2.array_), i1.pos_ - i2.pos_; 
    } 

private: 
    const Array* array_; 
    size_t pos_; 
}; 

template<typename T, size_t N> 
class array 
{ 
public: 
    using value_type = T; 
    using const_iterator = const array_iterator<array>; 

    constexpr value_type const& 
    operator[] (size_t idx) const noexcept 
    { return array_[idx]; } 

    constexpr const_iterator begin() const noexcept 
    { return const_iterator(*this); } 

    constexpr const_iterator end() const noexcept 
    { return const_iterator(*this, N); } 

    T array_[N]; 
    static constexpr size_t size = N; 
}; 

} 

int main() 
{ 
    static constexpr my::array<int, 3> array{{0,2,3}}; 

    static_assert (
    find (array.begin(), array.end(), 2) - array.begin() == 1, 
    "error"); 
} 

तुम भी जाँच करने के लिए दिलचस्पी हो सकती है Sprout library, इसमें बहुत सारे समेकित डेटा संरचनाएं और एल्गोरिदम शामिल हैं।

+0

एक कामकाज के लिए धन्यवाद, लेकिन मुझे C++ 11 constexpr खोजने की आवश्यकता नहीं है। बीटीडब्ल्यू यह जीसीसी बग है, इसलिए, जीसीसी कोड के साथ केवल कामकाज के साथ काम करेगा। – ForEveR

+0

@ForEveR: मुझे यकीन नहीं है कि यह एक बग है या नहीं। यह संभव है कि यह कुछ clang भाषा विस्तार या clang में आराम से std कार्यान्वयन है। मैंने अपना जवाब अपडेट किया और वास्तव में न्यूनतम कोड स्निपेट जोड़ा जो जीसीसी में संकलन त्रुटियों को ट्रिगर करता है। –

+0

हो सकता है ... लेकिन वैसे भी संबंधित: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67376 बग हो सकता है, हो सकता है। – ForEveR

संबंधित मुद्दे