Preferred way of initialization in c++11 [closed] - c++

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
int i = 0; // (a) Old C style should I use it?
int i{0}; // (b) Brace direct init
int i{}; // (c) Same as (b)
int i = {0}; // (d) as (b)
int i = {}; // (e) as (c)
auto i = 0; // (f) auto = int in this case.
auto i = int{0}; // (g) auto = more specific.
auto i = int{}; // (h) same as above (g)
Which one to use?
Sutter says use:
int i = 0;
auto i = 0;
Why not:
int i = {0};
auto i = int{0};
And should I get rid of "=" in some cases:
int i{0};
auto i{0}; // i is not what some might expect in this case. So I would prefer using "=" everywhere possible like int i = {0}; ...
EDIT:
This is what I'm aiming for it seems to me the most consistent:
rectangle w = { origin(), extents() };
complex<double> c = { 2.71828, 3.14159 };
mystruct m = { 1, 2 };
int a[] = { 1, 2, 3, 4 };
vector<int> v = { 1, 2, 3, 4 };
point p = {}; // Default initializes members
int i = {0}; // Checked assembly for this and it's binary the same as int i{0}; could be written also as int i = {};
string s = {""}; // Same as string s = {}; (OR) string s;
Real life examples:
std::string title = { pt.get<std::string>("document.window.title") };
const std::string file = { R"(CoreSettings.xml)" };
int_least64_t currentTick = { 0 }; // (OR) int_least64_t currentTick = {};
bool isRunning = { false }; // (OR) bool isRunning = {};
App* app = { nullptr }; // (OR) App* app = {};
Event event = {};
double detectedFrameRate = { 1000000000.0 / (swapIntervalDeltaCumulative / 20.0) };
double precision = { static_cast<double>(boost::chrono::high_resolution_clock::period::num)
/ boost::chrono::high_resolution_clock::period::den };
auto timeSpan = boost::chrono::duration_cast<boost::chrono::nanoseconds>(nowTime - startTime);
Alternative would be:
std::string title { pt.get<std::string>("document.window.title") };
const std::string file { R"(CoreSettings.xml)" };
int_least64_t currentTick { 0 }; // (OR) int_least64_t currentTick{};
bool isRunning { false }; // (OR) bool isRunning{};
App* app { nullptr }; // (OR) App* app{};
Event event {};
double detectedFrameRate { 1000000000.0 / (swapIntervalDeltaCumulative / 20.0) };
double precision { static_cast<double>(boost::chrono::high_resolution_clock::period::num)
/ boost::chrono::high_resolution_clock::period::den };
auto timeSpan = boost::chrono::duration_cast<boost::chrono::nanoseconds>(nowTime - startTime);
If not using braces it's ugly or error-prone:
int_least64_t currentTick = 0; // C style - changed this from double to int recently and compiler did not complain so I had something like int_least64_t currentTick = 0.0; ugly!
bool isRunning = false; // C style
App* app = nullptr; // C mixed with C++11 style;
Event event; // might not be initialized by all compilers
int someInt = func(); // func() returns double no error but narrowing.

For something simple, such as the int in your example, I'd agree that
int i=0;
is probably the most commonly understood (among programmers), but there are advantages to using the brace-initialization that, to me, make it preferable. For instance
int i = 3.99; // i gets 3; no warning, no error
int i{3.99}; // i gets 3; warning: "narrowing conversion"
It helps to write more bug-free code and is therefore a better way to do it in my view.
Mixing it with auto is more perilous. I typically use auto only for:
the temporary variable in a range-for loop (e.g. for (const auto &n : mycollection))
to simplify declaration of a named lambda
for iterator instances when I use them explicitly (rather than range-for)
templated code where doing so avoids creating a lengthy typedef

There are some wrong derivations:
auto i{0}; // [comment omitted]
int i();
The first one defines i as a std::initializer_list<int>.
The second declares an extern function named i returning int and having no arguments.
Rules-of-thumb:
Use auto where it saves typing and the type or its behavior is obvious. Examples:
auto x = new mymegathingy;
auto y = container.begin();
auto z = filestream.seekoff(0, basic_ios::curr);
Use assignment where that works (A potential temporary will be optimized away by any current compiler, possible when lhs and rhs have different types).
int i = 0;
int* i = 0; // For many types passing `nullptr` is better.
Use universal initializer syntax where assignment does not work.
std::vector<int> i = {1,2,3};
auto i = new int[]{1,2,3};
You might want to use direct constructor call where at least one obviously non-type argument is given, to avoid curly braces:
int i(0);
Beware that initializing with universal initializer syntax mixes bad with auto, we get a std::initializer_list<>:
auto i{0};
Avoid old-style init wherever you do not pass at least one obvious non-type argument, otherwise you risk inadvertently declaring a function:
int i();

For the int type variables i = 0 and i = {0} are the same. int i = 0 will be the most readable as this is what people are used to seeing.
If you do go down the auto route you need to be aware of the fact that auto i{0} and auto i = 0 are actually defining different types. (see Deduplicator's comment)
See this code:
#include <iostream>
#include <typeinfo>
int main(){
auto a = 0;
std::cout << "a is of type:" << typeid(a).name() << std::endl;
auto b = int{0};
std::cout << "b is of type:" << typeid(b).name() << std::endl;
auto c{0};
std::cout << "c is of type:" << typeid(c).name() << std::endl;
}
When we run this we get:
a is of type:i
b is of type:i
c is of type:St16initializer_listIiE
The auto c{0} is actually creating a std::initializer_list<int> which is almost certainly not what was expected here by the person who posted the question.
Basically there are a bunch of potentially nasty things here when it comes to readability.
Here's something I just compiled with g++ -Wall -std=c++11 main.cpp (g++ version 4.7.2)
#include <iostream>
#include <typeinfo>
#include <vector>
class d{
public:
std::vector<int> v;
d(std::initializer_list<int> l) : v(l) {
std::cout << "constructed class d with a " << l.size() << "-element list\n";
}
};
int main(){
auto d{0};
std::cout << "d is of type:" << typeid(d).name() << std::endl;
}
When we run this we get:
d is of type:St16initializer_listIiE
This might not be what you expected either. Clearly if you are writing production code you would want to choose better class names, but I was surprised this gave no warnings when being compiled.

I agree with Deduplicator.
I would use:
int i = 0;
It is the simplest, smallest, and most well known way of coding.

My recommendations are:
1) Use auto only for template constructions, where you can actually benefit from it and for iterators where type names may be long (if you prefer). Do not use it for primitive types, especially not references. If a function returns a reference and you want to use auto, you need to use auto&. If a not a copy is made, which can be quite tricky.
2) Think of code as someting that you can read on a piece of paper and be explicit about it. Do not try to hide types if they are fixed.
3) Initializer list can be dangerous. Use them when you need it. I have seen many structures, which are initialized using initializer lists and then a developer have added another member. With some compilers, no warnings are thrown (bummer).

Use auto name = initializer; exclusively. There are a couple places where you need auto&& or auto name = (Base*)initializer;. And definitely do not ever use the curly braces because uniform initialization is broken as hell.

Related

auto array declaration not allowable? [duplicate]

I have been playing with auto and I noticed that for most cases you can replace a variable definition with auto and then assign the type.
In the following code w and x are equivalent (default initialized int, but lets not get into potential copies). Is there a way to declare z such that it has the same type as y?
int w{};
auto x = int{};
int y[5];
auto z = int[5];
TL;DR
template<typename T, int N> using raw_array = T[N];
auto &&z = raw_array<int,5>{};
Your example of auto z = int[5]; isn't legal any more than auto z = int; is, simply because a type is not a valid initializer. You can write: auto z = int{}; because int{} is a valid initializer.
Once one realizes this, the next attempt would be:
auto z = int[5]{};
Note that your int y[5] does not have any initializer. If it had then you would have jumped straight here.
Unfortunately this does not work either for obscure syntax reasons. Instead you must find a legal way to name the array type in an initializer. For example, a typedef name can be used in an initializer. A handy reusable template type alias eliminates the burdensome requirement of a new typedef for every array type:
template<typename T, int N> using raw_array = T[N];
auto z = raw_array<int,5>{};
Aside: You can use template type aliases to fix the weird 'inside-out' syntax of C++, allowing you to name any compound type in an orderly, left-to-right fashion, by using this proposal.
Unfortunately due to the design bug in C and C++ which causes array-to-pointer conversions at the drop of a hat, the deduced type of the variable z is int* rather int[5]. The resulting variable becomes a dangling pointer when the temporary array is destroyed.
C++14 introduces decltype(auto) which uses different type deduction rules, correctly deducing an array type:
decltype(auto) z = raw_array<int,5>{};
But now we run into another design bug with arrays; they do not behave as proper objects. You can't assign, copy construct, do pass by value, etc., with arrays. The above code is like saying:
int g[5] = {};
int h[5] = g;
By all rights this should work, but unfortunately built-in arrays behave bizarrely in C and C++. In our case, the specific problem is that arrays are not allowed to have just any kind of initializer; they are strictly limited to using initializer lists. An array temporary, initialized by an initializer list, is not itself an initializer list.
Answer 1:
At this point Johannes Schaub makes the excellent suggestion that we can use temporary lifetime extension.
auto &&z = raw_array<int,5>{};
decltype(auto) isn't needed because the addition of && changes the deduced type, so Johannes Schaub's suggestion works in C++11. This also avoids the limitation on array initializers because we're initializing a reference instead of an array.
If you want the array to deduce its length from an initializer, you can use an incomplete array type:
template<typename T> using unsized_raw_array = T[];
auto &&z = unsized_raw_array<int>{1, 2, 3};
Although the above does what you want you may prefer to avoid raw arrays entirely, due to the fact that raw arrays do not behave like proper C++ objects, and the obscurity of their behavior and the techniques used above.
Answer 2:
The std::array template in C++11 does act like a proper object, including assignment, being passable by value, etc., and just generally behaving sanely and consistently where built-in arrays do not.
auto z = std::array<int,5>{};
However, with this you miss out on being able to have the array type infer its own length from an initializer. Instead You can write a make_array template function that does the inference. Here's a really simple version I haven't tested and which doesn't do things you might want, such as verify that all the arguments are the same type, or let you explicitly specify the type.
template<typename... T>
std::array<typename std::common_type<T...>::type, sizeof...(T)>
make_array(T &&...t) {
return {std::forward<T>(t)...};
}
auto z = make_array(1,2,3,4,5);
Not quite the same, but you could use array:
auto z = std::array<int, 5>();
decltype works with g++ 4.9.0 20130601 for this:
#include <iostream>
#include <algorithm>
static std::ostream& logger = std::clog;
class A {
static int _counter;
int _id;
public:
A() : _id(++_counter) {
logger << "\tA #" << _id << " c'tored\n";
}
~A() {
//logger << "\tA #" << _id << " d'tor\n";
}
inline int id() const{
return _id;
}
};
int A::_counter(0);
std::ostream& operator<<(std::ostream& os, const A& a) {
return os << a.id();
}
int main() {
auto dump = [](const A& a){ logger << a << " ";};
logger << "x init\n";
A x[5];
logger << "x contains: "; std::for_each(x, x+5, dump);
logger << "\ndecltype(x) y init\n";
decltype(x) y;
logger << "y contains: "; std::for_each(y, y+5, dump);
logger << std::endl;
return 0;
}
Output:
x init
A #1 c'tored
A #2 c'tored
A #3 c'tored
A #4 c'tored
A #5 c'tored
x contains: 1 2 3 4 5
decltype(x) y init
A #6 c'tored
A #7 c'tored
A #8 c'tored
A #9 c'tored
A #10 c'tored
y contains: 6 7 8 9 10
Not exactly the same thing, and it's a bit ugly, but it is possible to deduce the element type from a list initializer and declare the array directly, as follows:
template<typename T>
struct array_trait
{
using element_type = T;
array_trait(T(&&)[]);
};
decltype(array_trait({4,5,7}))::element_type a[] = {4,5,7};
std::cout << typeid(a).name() << std::endl;
for (unsigned i = 0; i < 3; i++)
std::cout << a[i] << std::endl;
The type should be int[3] and the output should be 4 5 7.
Better think about make_something from c++14
#include<iostream>
#include<experimental/array>
using namespace std;
using namespace std::experimental;
int main()
{
auto arr = make_array(1,2,3);
cout << arr.front() << endl;
return 0;
}

difference between { } and equal sign variables

I'm somewhat new to c++ programming. I couldn't find my answer any where on google so hopefully it can be answered here.
is there a difference between the following
unsigned int counter{ 1 };
or
unsigned int counter = 1;
the book uses the first option and its confusing to me because it doesn't explain the difference. below is the following code from the book i'm following.
#include <iostream>
#include <iomanip>
#include <cstdlib> // contains function prototype for rand()
using namespace std;
int main()
{
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
cout << setw(10) << (1 + rand() % 6);
// if counter is divisible by 5, start a new line of output
if (counter % 5 == 0) {
cout << endl;
}
}
}
Yes, they are two different types of initialization in C++.
The first one, i.e., unsigned int counter{ 1 }; is a direct initialization.
Whereas, the second one, i.e., unsigned int counter = 1;, is a copy initialization.
For all the details you can directly refer to the documentation.
However, I can emphasize:
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.
Initialization references here
For unsigned int type (such in your case), there are no real differences between the two initializations.
Note
The usage of curly braces in the first statement (unsigned int counter{ 1 }) provides an additional constraint:
Otherwise (if T is not a class type), if the braced-init-list has only one element [...], T is direct-initialized [...], except that narrowing conversions are not allowed.
In other words, the usage of curly braces in the initialization does not allow data looseness.
That is:
unsigned int counter{ 12.3 }; // error!!!
won't compile because you are trying to initialize an integer with a floating-point value.
Note this is a "property" of curly braces in the initialization. It is not strictly related to the initialization type.
In fact, you can also write:
unsigned int counter = { 12.3 }; // error!
which is, instead, a copy initialization, but having curly braces does not allows narrowing conversions.
Adding to the other explanations above.
I would use the first option(unsigned int counter{ 1 };) to initialize a list of values for a variable and for a single value, I would prefer to use the second option(unsigned int counter = 1;)
Hope this helps.
Consider the following demonstrative program.
#include <iostream>
struct A
{
int x;
explicit A( int x = 0 ) : x( x ) {}
};
int main()
{
A a1 { 10 };
std::cout << "a1.x = " << a1.x << '\n';
// A a2 = { 10 };
}
In this declaration
A a1 { 10 };
there is used the direct initialization.
And in the commented declaration
// A a2 = { 10 };
that can be also rewritten like
// A a2 = 10;
there is used the copy-initialization. But the constructor declared with the specifier explicit. So the compiler will issue an error. That is it is unable to convert the integer object 10 to the type of A implicitly.
You could write instead
A a2 = A{ 10 };
That is calling the constructor explicitly.
Of course for fundamental types there is no difference because neither constructor is applied except that narrowing conversion is not allowed when the braced initialization is used as for example
int x { 1.0 };
There is a big difference when the type specifier is the placeholder auto.
For example
auto x = 10;
x has the type int.
auto x { 10 };
x again has the type int.
auto x = { 10 };
Now x has the type std::initializer_list<int>.
For example you could rewrite your loop
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
the following way
for ( auto counter{ 1u }; counter <= 20; ++counter) {
but you may not write
for ( auto counter = { 1u }; counter <= 20; ++counter) {
because in this case the type of the variable counter is std::initializer_list<unsigned int>.
So in general you have the following forms of initializations
T x = value;
T x = { value };
T x( value );
T x { value };
For example
#include <iostream>
int main()
{
int x1 = 1;
std::cout << "x1 = " << x1 << '\n';
int x2 = ( 2 );
std::cout << "x2 = " << x2 << '\n';
int x3( 3 );
std::cout << "x3 = " << x3 << '\n';
int x4{ 4 };
std::cout << "x4 = " << x4 << '\n';
}
The program output is
x1 = 1
x2 = 2
x3 = 3
x4 = 4
But there is one more situation when instead of T() you should use T{} as an initializer. It is when template functions are used.
Consider the following demonstrative program
#include <iostream>
template <class>
void f()
{
std::cout << "f<T>() is called\n";
}
template <int>
void f()
{
std::cout << "f<int>() is called\n";
}
int main()
{
f<int()>();
f<int{}>();
}
Its output is
f<T>() is called
f<int>() is called
The construction int() used as a template argument specifiers the type template argument int while the construction int{} used as a template argument specifiers a non-type template argument of the type int equal to 0.
unsigned int counter = 1 ;
This style of initialization is inherited from C language.
unsigned int counter {1} ;
This style of initialization is of C++.
The difference is if you provide a wrong value while using C++ style initialization
for example:
unsigned int counter {-1} ;
This will give error (use -std=c++11 to compile it)
But this will not give any error.
unsigned int counter = -1 ;

Unable to instantiate generic function in a loop

I have some generic function, which looks similar to
template<int P>
struct foo {
static const int value = ...;
};
Now, I want to call this generic function in a loop. Like so:
for (int i = 0; i < 100; ++i)
{
auto bar = foo<i>::value;
}
But I get a lot of error messages. Like
the value of 'i' is not used in a constant expression
int i is not constant
I tried to fix it with:
foo<(const int)i>::value;
But to no avail. So, what is wrong with that and how can I make it work?
You cannot do it this way.
for (int i = 0; i < 100; ++i)
{
auto bar = foo<i>::value;
}
i needs to me a constant expression, so that the compiler can generate the code for it when it compiles your program.
Here's an exhaustive explanation of what constant expressions are:
http://en.cppreference.com/w/cpp/language/constant_expression
Here's a segment from the site:
int n = 1;
std::array<int, n> a1; // error: n is not a constant expression
const int cn = 2;
std::array<int, cn> a2; // OK: cn is a constant expression
So to make it happen compile time, you need to make your loop into a template recursion using variadic templates.
Maybe you can understand what you need to do better if you read this example:
// Example program
#include <iostream>
#include <string>
template<int P>
struct foo
{
static const int value = P;
};
template <int TIndex>
int call()
{
return foo<TIndex>::value;
}
template <int TIndex, int TIndex2, int ...Rest>
int call ()
{
return call<TIndex>() + call<TIndex2, Rest...>();
}
int main()
{
std::cout << "Test: " << call<1, 2>() << "\n"; // prints "Test: 3"
}
Nicky C posted a link to another question. It has a good answer and I don't feel right in copying it here. Take a look at my working example above and then look at the answer here:
https://stackoverflow.com/a/11081785/493298
You should be able to make it work. It's a bit of a syntax hell, but you can manage it, I'm sure.

How to use template data type inside main function in C++?

#include <iostream>
using namespace std;
template <class U>
U add (U a, U b)
{
U c = 0 ;
c = a + b;
return c;
}
int main()
{
int first = 2;
int second = 2;
U result = 0;
result = add(first, second);
cout << result << endl;
return 0;
}
I want to declare the data type of result variable using the template data type so that my addition program is generic but the compiler is giving me this error "result was not declared in this scope."
What you are trying to do is not possible. You can only use U within your add function.
However, you can do this instead
auto result = add(first, second);
Or
decltype(auto) result = add(first, second);
In your case both will do the same. However, they are quite different. To make it short, decltype(auto) will always get you the exact type returned by add, while auto may not.
Quick example:
const int& test()
{
static int c = 0;
return c;
}
// result type: int
auto result = test();
// result type: const int&
decltype(auto) result = test();
If you want to know more about auto, Scott Meyers explains it perfectly:
CppCon 2014: Scott Meyers "Type Deduction and Why You Care"
An alternative to José's excellent proposal, which would allow you to split the declaration and "initialisation" (which would then only be an assignment, as in your question), is:
decltype(add(first, second)) result = 0;
result = add(first, second);
But, obviously, yuck.

How to declare array with auto

I have been playing with auto and I noticed that for most cases you can replace a variable definition with auto and then assign the type.
In the following code w and x are equivalent (default initialized int, but lets not get into potential copies). Is there a way to declare z such that it has the same type as y?
int w{};
auto x = int{};
int y[5];
auto z = int[5];
TL;DR
template<typename T, int N> using raw_array = T[N];
auto &&z = raw_array<int,5>{};
Your example of auto z = int[5]; isn't legal any more than auto z = int; is, simply because a type is not a valid initializer. You can write: auto z = int{}; because int{} is a valid initializer.
Once one realizes this, the next attempt would be:
auto z = int[5]{};
Note that your int y[5] does not have any initializer. If it had then you would have jumped straight here.
Unfortunately this does not work either for obscure syntax reasons. Instead you must find a legal way to name the array type in an initializer. For example, a typedef name can be used in an initializer. A handy reusable template type alias eliminates the burdensome requirement of a new typedef for every array type:
template<typename T, int N> using raw_array = T[N];
auto z = raw_array<int,5>{};
Aside: You can use template type aliases to fix the weird 'inside-out' syntax of C++, allowing you to name any compound type in an orderly, left-to-right fashion, by using this proposal.
Unfortunately due to the design bug in C and C++ which causes array-to-pointer conversions at the drop of a hat, the deduced type of the variable z is int* rather int[5]. The resulting variable becomes a dangling pointer when the temporary array is destroyed.
C++14 introduces decltype(auto) which uses different type deduction rules, correctly deducing an array type:
decltype(auto) z = raw_array<int,5>{};
But now we run into another design bug with arrays; they do not behave as proper objects. You can't assign, copy construct, do pass by value, etc., with arrays. The above code is like saying:
int g[5] = {};
int h[5] = g;
By all rights this should work, but unfortunately built-in arrays behave bizarrely in C and C++. In our case, the specific problem is that arrays are not allowed to have just any kind of initializer; they are strictly limited to using initializer lists. An array temporary, initialized by an initializer list, is not itself an initializer list.
Answer 1:
At this point Johannes Schaub makes the excellent suggestion that we can use temporary lifetime extension.
auto &&z = raw_array<int,5>{};
decltype(auto) isn't needed because the addition of && changes the deduced type, so Johannes Schaub's suggestion works in C++11. This also avoids the limitation on array initializers because we're initializing a reference instead of an array.
If you want the array to deduce its length from an initializer, you can use an incomplete array type:
template<typename T> using unsized_raw_array = T[];
auto &&z = unsized_raw_array<int>{1, 2, 3};
Although the above does what you want you may prefer to avoid raw arrays entirely, due to the fact that raw arrays do not behave like proper C++ objects, and the obscurity of their behavior and the techniques used above.
Answer 2:
The std::array template in C++11 does act like a proper object, including assignment, being passable by value, etc., and just generally behaving sanely and consistently where built-in arrays do not.
auto z = std::array<int,5>{};
However, with this you miss out on being able to have the array type infer its own length from an initializer. Instead You can write a make_array template function that does the inference. Here's a really simple version I haven't tested and which doesn't do things you might want, such as verify that all the arguments are the same type, or let you explicitly specify the type.
template<typename... T>
std::array<typename std::common_type<T...>::type, sizeof...(T)>
make_array(T &&...t) {
return {std::forward<T>(t)...};
}
auto z = make_array(1,2,3,4,5);
Not quite the same, but you could use array:
auto z = std::array<int, 5>();
decltype works with g++ 4.9.0 20130601 for this:
#include <iostream>
#include <algorithm>
static std::ostream& logger = std::clog;
class A {
static int _counter;
int _id;
public:
A() : _id(++_counter) {
logger << "\tA #" << _id << " c'tored\n";
}
~A() {
//logger << "\tA #" << _id << " d'tor\n";
}
inline int id() const{
return _id;
}
};
int A::_counter(0);
std::ostream& operator<<(std::ostream& os, const A& a) {
return os << a.id();
}
int main() {
auto dump = [](const A& a){ logger << a << " ";};
logger << "x init\n";
A x[5];
logger << "x contains: "; std::for_each(x, x+5, dump);
logger << "\ndecltype(x) y init\n";
decltype(x) y;
logger << "y contains: "; std::for_each(y, y+5, dump);
logger << std::endl;
return 0;
}
Output:
x init
A #1 c'tored
A #2 c'tored
A #3 c'tored
A #4 c'tored
A #5 c'tored
x contains: 1 2 3 4 5
decltype(x) y init
A #6 c'tored
A #7 c'tored
A #8 c'tored
A #9 c'tored
A #10 c'tored
y contains: 6 7 8 9 10
Not exactly the same thing, and it's a bit ugly, but it is possible to deduce the element type from a list initializer and declare the array directly, as follows:
template<typename T>
struct array_trait
{
using element_type = T;
array_trait(T(&&)[]);
};
decltype(array_trait({4,5,7}))::element_type a[] = {4,5,7};
std::cout << typeid(a).name() << std::endl;
for (unsigned i = 0; i < 3; i++)
std::cout << a[i] << std::endl;
The type should be int[3] and the output should be 4 5 7.
Better think about make_something from c++14
#include<iostream>
#include<experimental/array>
using namespace std;
using namespace std::experimental;
int main()
{
auto arr = make_array(1,2,3);
cout << arr.front() << endl;
return 0;
}