programing

벡터 변환

nicescript 2021. 1. 16. 09:19
반응형

벡터 변환 initializer_list에


모두가 만들어 std::vector에서 std::initializer_list다른 방법으로 주위에 대해 무엇을, 그러나?

예. std::initializer_list매개 변수로 사용하는 경우 :

void someThing(std::initializer_list<int> items)
{
...
}

vector<T>리터럴 목록 대신에 항목이있는 경우가 있습니다 .

std::vector<int> v;
// populate v with values
someThing(v); // boom! No viable conversion etc.

보다 일반적인 질문은 stl::initializer_list.NET뿐만 아니라 STL iterable에서 를 만드는 방법 std::vector입니다.


나는 작동하는 것처럼 보였지만 불행히도 initializer_lists가 로컬 범위의 값 사본에 대한 참조로 취급되는 방식으로 인해 메모리 액세스 위반을 일으켰습니다.

여기에 대안이 있습니다. 가능한 각 항목 수에 대해 별도의 함수 및 별도의 정적 이니셜 라이저 목록이 생성되며 매개 변수 팩으로 계산됩니다. 이것은 스레드로부터 안전하지 않으며 const_cast (매우 나쁜 것으로 간주 됨)를 사용하여 정적 initializer_list 메모리에 기록합니다. 그러나 gcc와 clang 모두에서 깨끗하게 작동합니다.

모호한 이유로이 문제를 해결해야하고 다른 옵션이없는 경우이 해킹을 시도 할 수 있습니다.

#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <vector>

namespace __range_to_initializer_list {

    constexpr size_t DEFAULT_MAX_LENGTH = 128;

    template <typename V> struct backingValue { static V value; };
    template <typename V> V backingValue<V>::value;

    template <typename V, typename... Vcount> struct backingList { static std::initializer_list<V> list; };
    template <typename V, typename... Vcount>
    std::initializer_list<V> backingList<V, Vcount...>::list = {(Vcount)backingValue<V>::value...};

    template <size_t maxLength, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) >= maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        throw std::length_error("More than maxLength elements in range.");
    }

    template <size_t maxLength = DEFAULT_MAX_LENGTH, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) < maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        if (current != end)
            return generate_n<maxLength, It, V, V, Vcount...>(begin, end, ++current);

        current = begin;
        for (auto it = backingList<V,Vcount...>::list.begin();
             it != backingList<V,Vcount...>::list.end();
             ++current, ++it)
            *const_cast<V*>(&*it) = *current;

        return backingList<V,Vcount...>::list;
    }

}

template <typename It>
std::initializer_list<typename It::value_type> range_to_initializer_list(It begin, It end)
{
    return __range_to_initializer_list::generate_n(begin, end, begin);
}

int main()
{
    std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
    std::initializer_list<int> list = range_to_initializer_list(vec.begin(), vec.end());
    for (int i : list)
        std::cout << i << std::endl;
    return 0;
}

대답은 아니오입니다. 그렇게 할 수 없습니다.

유형의 객체 std::initializer_list<T>타입 T.의 물체들의 어레이에 대한 액세스 제공하는 경량 프록시 목적 std::initializer_list되는 오브젝트를 자동적으로 생성 했을 때

  • a braced-init-list is used in list-initialization, including function-call list initialization and assignment expressions (not to be confused with constructor initializer lists)
  • a braced-init-list is bound to auto, including in a ranged for loop

As far as library support goes, std::initializer_list only has a default constructor that constructs an empty list, and its iterators are constant. The lack of a push_back() member means you cannot apply e.g. a std::copy with a std::back_inserter iterator adaptor to fill it, and neither can you assign through such iterators directly:

#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <vector>

int main() 
{
    auto v = std::vector<int> { 1, 2 };
    std::initializer_list<int> i;
    auto it = std::begin(i);
    *it = begin(v); // error: read-only variable is not assignable
}

Live Example

If you look at the Standard Containers, in addition to accepting std::initializer_list in their constructors / inserters, they all have constructors / inserters taking an iterator pair, and the implementation is likely to delegate the initializer_list function to the corresponding iterator pair function. E.g. the std::vector<T>::insert function in libc++ is this simple one-liner:

 iterator insert(const_iterator __position, initializer_list<value_type> __il)
        {return insert(__position, __il.begin(), __il.end());}

You should modify your code along similar lines:

void someThing(std::initializer_list<int> items)
{
    someThing(items.begin(), items.end()); // delegate
}

template<class It>
void someThing(It first, It last)
{
    for (auto it = first, it != last; ++it) // do your thing
}

In times when you have your items in a vector instead of a literal list:

std::vector<int> v = { 1, 2 };
auto i = { 1, 2 };
someThing(begin(v), end(v)); // OK
someThing(i); // also OK
someThing({1, 2}); // even better

Apparently no, it is not possible. There is no such constructor (and I believe for good reasons), std::initializer_list is a weird creature.

What you could do instead is to change someThing() to accept a pair of iterators. In that way you get what you want, provided you can change the signature of that function (it isn't in a third party library, etc).


Yes you can do this, but you don't want to do it, because how you have to do it is pretty silly.

First, determine what the max length of your list is. There must be a max length, because size_t is not unbounded. Ideally find a better (smaller) one, like 10.

Second, write magic switch code that takes a run-time integer, and maps it to a compile time integer, and then invokes a template class or function with that compile time integer. Such code needs a max integer size -- use the max length above.

Now, magic switch the size of the vector into a compile time length.

Create a compile time sequence of integers, from 0 to length-1. Unpack that sequence in a initializer_list construction, each time invoking [] on the std::vector. Call your function with that resulting initializer_list.

The above is tricky and ridiculous and most compilers will blow up on it. There is one step I'm uncertain of the legality of -- is the construction of an initializer_list a legal spot to do varardic argument unpacking?

Here is an example of a magic switch: Can I separate creation and usage locations of compile-time strategies?

Here is an example of the indices, or sequence, trick: Constructor arguments from tuple

This post should only be of theoretical interest, because practically this is a really silly way to solve this problem.

Doing it with an arbitrary iterable is harder, without doing n^2 work. But as the above is already ridiculous enough, and the arbitrary iterable version would be more ridiculous... (Maybe with a pack of lambdas -- getting it so that the arguments are evaluated in order could be tricky. Is there a sequence point between the evaluation of the various arguments to an initializer list?)


For a vector you could use pointer arithmetic.

Foo( std::initializer_list< _Type >( aVector.data(), aVector.data() + aVector.size() ) ) ;

You could wrap this in a generic function.

template< typename _Type >
auto    InitList( const _Type * begin, size_t size )
{
  return std::initializer_list< _Type >( begin, begin + size ) ;
}

Call function like this.

Foo( InitList( aVector.data(), aVector.size() ) ) ;

If you don't mind copies, then I think something like this would work:

template<class Iterator>
using iterator_init_list = std::initializer_list<typename std::iterator_traits<Iterator>::value_type>;

template<class Iterator, class... Ts>
iterator_init_list<Iterator> to_initializer_list(Iterator start, Iterator last, Ts... xs)
{
    if (start == last) return iterator_init_list<Iterator>{xs...};
    else return to_initializer_list(start+1, last, xs..., *start);
}

ReferenceURL : https://stackoverflow.com/questions/18895583/convert-a-vectort-to-initializer-listt

반응형