std :: array를 사용하여 C 배열 초기화 "int arr [] = {e1, e2, e3,…}"동작을 에뮬레이트하는 방법?
(참고 :이 질문은 요소 수를 지정하지 않아도 중첩 된 유형을 직접 초기화 할 수 있습니다.)
이 질문 은 C 배열의 왼쪽 사용법을 설명합니다 int arr[20];
. 에 그의 대답 , C 배열의 마지막 거점의 @ 제임스 칸 세이 쇼 하나, 그것은 독특한 초기화 특성이있다 :
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
우리는 요소의 수를 지정할 필요가 없습니다. 이제 C ++ 11 함수 std::begin
와 std::end
from <iterator>
( 또는 자신의 변형 )을 사용하여 반복 하고 크기를 생각할 필요가 없습니다.
이제 같은 방법으로 달성 할 수있는 방법이 std::array
있습니까? 매크로를 사용하면 더보기 좋게 만들 수 있습니다. :)
??? std_array = { "here", "be", "elements" };
편집 : 다양한 답변으로 컴파일 된 중간 버전은 다음과 같습니다.
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
그리고 모든 종류의 멋진 C ++ 11을 사용합니다.
- Variadic 템플릿
sizeof...
- rvalue 참조
- 완벽한 전달
std::array
, 물론이야- 균일 한 초기화
- 균일 한 초기화로 리턴 유형 생략
- 타입 추론 (
auto
)
그러나 @Johannes가 @Xaade의 답변에 대한 의견에서 지적한 것처럼 이러한 함수로 중첩 유형을 초기화 할 수 없습니다. 예:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
또한 이니셜 라이저 수는 구현에서 지원하는 함수 및 템플릿 인수의 수로 제한됩니다.
내가 생각할 수있는 최선은 :
template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
std::array<T, 1 + sizeof...(Tail)> a = { head, tail ... };
return a;
}
auto a = make_array(1, 2, 3);
그러나이를 위해서는 컴파일러가 NRVO를 수행 한 다음 반환 된 값의 사본을 건너 뜁니다 (법적이지만 필수는 아님). 실제로 모든 C ++ 컴파일러가 직접 초기화만큼 빠르도록 최적화 할 수있을 것으로 기대합니다.
나는 간단한 것을 기대할 것이다 make_array
.
template<typename ret, typename... T> std::array<ret, sizeof...(T)> make_array(T&&... refs) {
// return std::array<ret, sizeof...(T)>{ { std::forward<T>(refs)... } };
return { std::forward<T>(refs)... };
}
이전 게시물의 몇 가지 아이디어를 결합하여 다음은 GCC4.6에서 테스트 한 중첩 구조에서도 작동하는 솔루션입니다.
template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
return std::array<T, sizeof...(Args) + 1>{ std::forward<T>(t), std::forward<Args>(args)...};
}
이상하게도 반환 값을 rvalue 참조로 만들 수 없으며 중첩 된 구성에는 작동하지 않습니다. 어쨌든, 여기 테스트가 있습니다 :
auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
);
std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]
(마지막 출력의 경우 pretty-printer 사용하고 있습니다.)
실제로,이 구조의 유형 안전을 개선하겠습니다. 모든 유형이 동일해야합니다. 한 가지 방법은 위에서 편집 한 정적 어설 션을 추가하는 것입니다. 다른 방법은 make_array
다음과 같이 유형이 동일한 경우 에만 활성화 하는 것입니다.
template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
return std::array<T, sizeof...(Args) + 1> { std::forward<T>(t), std::forward<Args>(args)...};
}
어느 쪽이든 variadic all_same<Args...>
유형 특성 이 필요합니다 . 그것은 여기에서 일반화이다 std::is_same<S, T>
(부패가 혼합 수 있도록하는 것이 중요합니다 T
, T&
, T const &
등) :
template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };
참고 make_array()
복사 - 임시 (충분한 최적화 플래그와 함께!)하는 컴파일러에 의해 반환이를 rvalue로 치료를 허용하거나 최적화 멀리, 그리고 std::array
컴파일러가 최적의 시공 방법을 선택 무료입니다, 그래서 집계 유형이 .
마지막으로, make_array
이니셜 라이저를 설정할 때 복사 / 이동 구성을 피할 수 없습니다 . 따라서 std::array<Foo,2> x{Foo(1), Foo(2)};
복사 / 이동은 없지만 auto x = make_array(Foo(1), Foo(2));
인수가 전달 될 때 두 개의 복사 / 이동이 make_array
있습니다. 난 당신이 도우미에 전적으로 가변 인자 초기화 목록을 통과 할 수 없기 때문에 당신이 그 개선 할 수 있다고 생각하지 않습니다 과 를 추론 유형 및 크기 - 프리 프로세서가 있던 경우에 sizeof...
가변 인수 기능을, 아마도 그 완료,하지만 수 핵심 언어 내에서.
후행 반환 구문을 사용하면 make_array
더 간단해질 수 있습니다
#include <array>
#include <type_traits>
#include <utility>
template <typename... T>
auto make_array(T&&... t)
-> std::array<std::common_type_t<T...>, sizeof...(t)>
{
return {std::forward<T>(t)...};
}
int main()
{
auto arr = make_array(1, 2, 3, 4, 5);
return 0;
}
불행히도 집계 클래스의 경우 명시 적 유형 지정이 필요합니다
/*
struct Foo
{
int a, b;
}; */
auto arr = make_array(Foo{1, 2}, Foo{3, 4}, Foo{5, 6});
실제로이 make_array
구현은 sizeof ... 연산자로 나열됩니다 .
C ++ 17 버전
Thanks to template argument deduction for class templates proposal we can use deduction guides to get rid of make_array
helper
#include <array>
namespace std
{
template <typename... T> array(T... t)
-> array<std::common_type_t<T...>, sizeof...(t)>;
}
int main()
{
std::array a{1, 2, 3, 4};
return 0;
}
Compiled with -std=c++1z
flag under x86-64 gcc 7.0
I know it's been quite some time since this question was asked, but I feel the existing answers still have some shortcomings, so I'd like to propose my slightly modified version. Following are the points that I think some existing answers are missing.
1. No need to rely on RVO
Some answers mention that we need to rely on RVO to return the constructed array
. That is not true; we can make use of copy-list-initialization to guarantee there will never be temporaries created. So instead of:
return std::array<Type, …>{values};
we should do:
return {{values}};
2. Make make_array
a constexpr
function
This allow us to create compile-time constant arrays.
3. No need to check that all arguments are of the same type
First off, if they are not, the compiler will issue a warning or error anyway because list-initialization doesn't allow narrowing. Secondly, even if we really decide to do our own static_assert
thing (perhaps to provide better error message), we should still probably compare the arguments' decayed types rather than raw types. For example,
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array<int>(a, b, c); // Will this work?
If we are simply static_assert
ing that a
, b
, and c
have the same type, then this check will fail, but that probably isn't what we'd expect. Instead, we should compare their std::decay_t<T>
types (which are all int
s)).
4. Deduce the array value type by decaying the forwarded arguments
This is similar to point 3. Using the same code snippet, but don't specify the value type explicitly this time:
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
We probably want to make an array<int, 3>
, but the implementations in the existing answers probably all fail to do that. What we can do is, instead of returning a std::array<T, …>
, return a std::array<std::decay_t<T>, …>
.
There is one disadvantage about this approach: we can't return an array
of cv-qualified value type any more. But most of the time, instead of something like an array<const int, …>
, we would use a const array<int, …>
anyway. There is a trade-off, but I think a reasonable one. The C++17 std::make_optional
also takes this approach:
template< class T >
constexpr std::optional<std::decay_t<T>> make_optional( T&& value );
Taking the above points into account, a full working implementation of make_array
in C++14 looks like this:
#include <array>
#include <type_traits>
#include <utility>
template<typename T, typename... Ts>
constexpr std::array<std::decay_t<T>, 1 + sizeof... (Ts)>
make_array(T&& t, Ts&&... ts)
noexcept(noexcept(std::is_nothrow_constructible<
std::array<std::decay_t<T>, 1 + sizeof... (Ts)>, T&&, Ts&&...
>::value))
{
return {{std::forward<T>(t), std::forward<Ts>(ts)...}};
}
template<typename T>
constexpr std::array<std::decay<T>_t, 0> make_array() noexcept
{
return {};
}
Usage:
constexpr auto arr = make_array(make_array(1, 2),
make_array(3, 4));
static_assert(arr[1][1] == 4, "!");
C++11 will support this manner of initialization for (most?) std containers.
(Solution by @dyp)
Note: requires C++14 (std::index_sequence
). Although one could implement std::index_sequence
in C++11.
#include <iostream>
// ---
#include <array>
#include <utility>
template <typename T>
using c_array = T[];
template<typename T, size_t N, size_t... Indices>
constexpr auto make_array(T (&&src)[N], std::index_sequence<Indices...>) {
return std::array<T, N>{{ std::move(src[Indices])... }};
}
template<typename T, size_t N>
constexpr auto make_array(T (&&src)[N]) {
return make_array(std::move(src), std::make_index_sequence<N>{});
}
// ---
struct Point { int x, y; };
std::ostream& operator<< (std::ostream& os, const Point& p) {
return os << "(" << p.x << "," << p.y << ")";
}
int main() {
auto xs = make_array(c_array<Point>{{1,2}, {3,4}, {5,6}, {7,8}});
for (auto&& x : xs) {
std::cout << x << std::endl;
}
return 0;
}
If std::array is not a constraint and if you have Boost, then take a look at list_of()
. This is not exactly like C type array initialization that you want. But close.
Create an array maker type.
It overloads operator,
to generate an expression template chaining each element to the previous via references.
Add a finish
free function that takes the array maker and generates an array directly from the chain of references.
The syntax should look something like this:
auto arr = finish( make_array<T>->* 1,2,3,4,5 );
It does not permit {}
based construction, as only operator=
does. If you are willing to use =
we can get it to work:
auto arr = finish( make_array<T>= {1}={2}={3}={4}={5} );
or
auto arr = finish( make_array<T>[{1}][{2}[]{3}][{4}][{5}] );
None of these look like good solutions.
Using variardics limits you to your compiler-imposed limit on number of varargs and blocks recursive use of {}
for substructures.
In the end, there really isn't a good solution.
What I do is I write my code so it consumes both T[]
and std::array
data agnostically -- it doesn't care which I feed it. Sometimes this means my forwarding code has to carefully turn []
arrays into std::array
s transparently.
'Programing' 카테고리의 다른 글
HTML 5 doctype을 사용하지 않는 이유는 무엇입니까? (0) | 2020.06.29 |
---|---|
github에서 풀 요청을 취소하는 방법은 무엇입니까? (0) | 2020.06.29 |
비밀번호를 저장하라는 메시지를 브라우저에 표시하려면 어떻게해야합니까? (0) | 2020.06.29 |
ORA-12505, TNS : listener가 현재 연결 설명자에 제공된 SID를 알지 못합니다. (0) | 2020.06.28 |
C #을 사용한 압축 / 압축 해제 문자열 (0) | 2020.06.28 |