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;
}
Related
I have seen this code:
template <class... TYPES>
constexpr void tuple<TYPES...>::swap(tuple& other)
noexcept((is_nothrow_swappable_v<TYPES> and ...))
{
for...(constexpr size_t N : view::iota(size_t(0), sizeof...(TYPES))) {
swap(get<N>(*this), get<N>(other));
}
}
What does the construct for... do?
This is not standard C++ as of 2021.
This is a proposed syntax (named an ‘expansion statement’) meant for what can be described as a more-or-less loop analogue of if constexpr: a compile-time loop construct, unrolled syntactically. Its advantage over ordinary loops is that it allows iterating over heterogeneously-typed structures.
This syntax would allow writing loops like the following:
std::tuple<int, const char *, double> t { 42, "hello", 69.420 };
for... (auto x : t) {
std::cout << x << std::endl;
}
meaning roughly the same as:
std::tuple<int, const char *, double> t { 42, "hello", 69.420 };
{
auto x = std::get<0>(t);
std::cout << x << std::endl;
}
{
auto x = std::get<1>(t);
std::cout << x << std::endl;
}
{
auto x = std::get<2>(t);
std::cout << x << std::endl;
}
In the for... loop above, in each iteration the x variable has a different type; it can be said that there is a separate, independent variable for each iteration. In an ordinary loop, the type of the variable is shared between iterations, which means the loop cannot pass type checking.
This syntax was first proposed in P1306R0, which also describes a related, but distinct for constexpr (the following revision unifies the two). P1858R1 introduced another syntax for it, template for, replacing the ellipsis syntax.
It is apparently expected that this feature will be included in C++23.
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;
}
#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.
I have some questions regarding this program:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
auto r=ref(x);
cout<<boolalpha;
cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
int x=5;
foo (x);
return 0;
}
The output is:
false
I want to know, if std::ref doesn't return the reference of an object, then what does it do? Basically, what is the difference between:
T x;
auto r = ref(x);
and
T x;
T &y = x;
Also, I want to know why does this difference exist? Why do we need std::ref or std::reference_wrapper when we have references (i.e. T&)?
Well ref constructs an object of the appropriate reference_wrapper type to hold a reference to an object. Which means when you apply:
auto r = ref(x);
This returns a reference_wrapper and not a direct reference to x (ie T&). This reference_wrapper (ie r) instead holds T&.
A reference_wrapper is very useful when you want to emulate a reference of an object which can be copied (it is both copy-constructible and copy-assignable).
In C++, once you create a reference (say y) to an object (say x), then y and x share the same base address. Furthermore, y cannot refer to any other object. Also you cannot create an array of references ie code like this will throw an error:
#include <iostream>
using namespace std;
int main()
{
int x=5, y=7, z=8;
int& arr[] {x,y,z}; // error: declaration of 'arr' as array of references
return 0;
}
However this is legal:
#include <iostream>
#include <functional> // for reference_wrapper
using namespace std;
int main()
{
int x=5, y=7, z=8;
reference_wrapper<int> arr[] {x,y,z};
for (auto a: arr)
cout << a << " ";
return 0;
}
/* OUTPUT:
5 7 8
*/
Talking about your problem with cout << is_same<T&,decltype(r)>::value;, the solution is:
cout << is_same<T&,decltype(r.get())>::value; // will yield true
Let me show you a program:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
int main()
{
cout << boolalpha;
int x=5, y=7;
reference_wrapper<int> r=x; // or auto r = ref(x);
cout << is_same<int&, decltype(r.get())>::value << "\n";
cout << (&x==&r.get()) << "\n";
r=y;
cout << (&y==&r.get()) << "\n";
r.get()=70;
cout << y;
return 0;
}
/* Ouput:
true
true
true
70
*/
See here we get to know three things:
A reference_wrapper object (here r) can be used to create an array of references which was not possible with T&.
r actually acts like a real reference (see how r.get()=70 changed the value of y).
r is not same as T& but r.get() is. This means that r holds T& ie as its name suggests is a wrapper around a reference T&.
I hope this answer is more than enough to explain your doubts.
std::reference_wrapper is recognized by standard facilities to be able to pass objects by reference in pass-by-value contexts.
For example, std::bind can take in the std::ref() to something, transmit it by value, and unpacks it back into a reference later on.
void print(int i) {
std::cout << i << '\n';
}
int main() {
int i = 10;
auto f1 = std::bind(print, i);
auto f2 = std::bind(print, std::ref(i));
i = 20;
f1();
f2();
}
This snippet outputs :
10
20
The value of i has been stored (taken by value) into f1 at the point it was initialized, but f2 has kept an std::reference_wrapper by value, and thus behaves like it took in an int&.
A reference (T& or T&&) is a special element in C++ language. It allows to manipulate an object by reference and has special use cases in the language. For example, you cannot create a standard container to hold references: vector<T&> is ill formed and generates a compilation error.
A std::reference_wrapper on the other hand is a C++ object able to hold a reference. As such, you can use it in standard containers.
std::ref is a standard function that returns a std::reference_wrapper on its argument. In the same idea, std::cref returns std::reference_wrapper to a const reference.
One interesting property of a std::reference_wrapper, is that it has an operator T& () const noexcept;. That means that even if it is a true object, it can be automatically converted to the reference that it is holding. So:
as it is a copy assignable object, it can be used in containers or in other cases where references are not allowed
thanks to its operator T& () const noexcept;, it can be used anywhere you could use a reference, because it will be automatically converted to it.
Added an example to show the difference in value you get when you pass the T& and ref(T) arguments in the bind function.
std::bind copies the argument provided unless it is passed by std::ref()/std::cref().
void f(int r1, int& r2, int w1, int& w2)
{
std::cout << r1 << r2 << w1 << w2; // 5 5 10 10
r1 = 9, r2 = 9, w1 = 9, w2 = 9;
}
int main()
{
int w1 = 5, w2 = 5, n1 = 5, n2 = 5;
int& r1 = n1;
int& r2 = n2;
std::function<void()> bound_f = std::bind(f, r1, r2, std::ref(w1), std::ref(w2));
r1 = 10, r2 = 10, w1 = 10, w2 = 10;
bound_f(); // 5 5 10 10
std::cout << r1 << r2 << w1 << w2; // 10 10 10 9
}
How can I make a raw pointer behave like a range, for a for-range loop syntax.
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null
Motivation:
It is now vox populi that an boost::optional (future std::optional) value can be viewed as a range and therefore used in a for range loop http://faithandbrave.hateblo.jp/entry/2015/01/29/173613.
When I rewrote my own simplified version of it:
namespace boost {
template <class Optional>
decltype(auto) begin(Optional& opt) noexcept{
return opt?&*opt:nullptr;
}
template <class Optional>
decltype(auto) end(Optional& opt) noexcept{
return opt?std::next(&*opt):nullptr;
}
}
Used as
boost::optional<int> opt = 3;
for (int& x : opt) std::cout << x << std::endl;
While looking that code I imagined that it could be generalized to raw (nullable) pointers as well.
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;
instead of the usual if(dptr) std::cout << *dptr << std::endl;. Which is fine but I wanted to achieve the other syntax above.
Attempts
First I tried to make the above Optional version of begin and end work for pointers but I couldn't. So I decided to be explicit in the types and remove all templates:
namespace std{ // excuse me, this for experimenting only, the namespace can be removed but the effect is the same.
double* begin(double* opt){
return opt?&*opt:nullptr;
}
double* end(double* opt){
return opt?std::next(&*opt):nullptr;
}
}
Almost there, it works for
for(double* ptr = std::begin(dptr); ptr != std::end(dptr); ++ptr)
std::cout << *ptr << std::endl;
But it doesn't work for the supposedly equivalent for-range loop:
for(double& d : dptr) std::cout << d << std::endl;
Two compilers tell me: error: invalid range expression of type 'double *'; no viable 'begin' function available
What is going on? Is there a compiler magic that forbids the ranged-loop to to work for pointers. Am I making a wrong assumption about the ranged-loop syntax?
Ironically, in the standard there is an overload for std::begin(T(&arr)[N]) and this is very close to it.
Note and a second though
Yes, the idea is silly because, even if possible this would be very confusing:
double* ptr = new double[10];
for(double& d : ptr){...}
would iterate over the first element only. A more clear and also realistic workaround would be to do something like workaround proposed by #Yakk:
for(double& d : boost::make_optional_ref(ptr)){...}
In this way it is clear that we are iterating over one element only and that that element is optional.
Ok, ok, I will go back to if(ptr) ... use *ptr.
Because the way that range-based for works is (from §6.5.4):
begin-expr and end-expr are determined as follows
— if _RangeT is an array type, [..]
— if _RangeT is a class type, [..]
— otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin
and end are looked up in the associated namespaces (3.4.2). [ Note: Ordinary unqualified lookup (3.4.1)
is not performed. —end note ]
What are the associated namespaces in this case? (§3.4.2/2, emphasis mine):
The sets of namespaces and classes are determined in the following way:
(2.1) — If T is a fundamental type, its associated sets of namespaces and classes are both empty.
Thus, there is no place to put your double* begin(double*) such that it will be called by the range-based for statement.
A workaround for what you want to do is just make a simple wrapper:
template <typename T>
struct PtrWrapper {
T* p;
T* begin() const { return p; }
T* end() const { return p ? p+1 : nullptr; }
};
for (double& d : PtrWrapper<double>{dptr}) { .. }
It is a useful lie to think that for(:) loops are implemented by "calling std::begin and std::end in a ADL-activated context". But that is a lie.
The standard instead basically does a parallel implementation of the std::begin and std::end in itself. This prevents the language's low level constructs from depending on its own library, which seems like a good idea.
The only lookup for begin by the language is the ADL-based lookup. Your pointer's std::begin won't be found, unless you are a pointer to something in std. The std::begin( T(&)[N} ) isn't found this way by the compiler, but instead that iteration is hard-coded by the language.
namespace boost {
template<class T>
T* begin( optional<T>&o ) {
return o?std::addressof(*o):nullptr;
}
template<class T>
T* begin( optional<T&>&&o ) {
return o?std::addressof(*o):nullptr;
}
template<class T>
T const* begin( optional<T> const&o ) {
return o?std::addressof(*o):nullptr;
}
template<class T>
T* end( optional<T>&o ) {
return o?std::next(begin(o)):nullptr;
}
template<class T>
T* end( optional<T&>&&o ) {
return o?std::next(begin(o)):nullptr;
}
template<class T>
T const* end( optional<T> const&o ) {
return o?std::next(begin(o)):nullptr;
}
template<class T>
boost::optional<T&> as_optional( T* t ) {
if (t) return *t;
return {};
}
}
now you can:
void foo(double * d) {
for(double& x : boost::as_optional(d)) {
std::cout << x << "\n";
}
without having to repeat the type double.
Note that an rvalue optional to a non-reference returns a T const*, while an rvalue optonal to a T& returns a T*. Iterating over a temporary in a writing context is probably an error.
TL;DR
This construct can be used in a range for loop:
std::views::counted(raw_ptr, !!raw_ptr)
Details
C++20 offers a plethora of ways to create ad-hoc iterables, using the ranges library. For example:
#include <ranges>
#include <iostream>
int main()
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *raw_ptr = a;
for(int i : std::views::counted(raw_ptr, 10))
std::cout << i << ' ';
std::cout << '\n';
for(int i : std::views::counted(raw_ptr, 1))
std::cout << i << ' ';
std::cout << '\n';
std::cout << "empty for null pointer pointer\n";
raw_ptr = nullptr;
for(int i : std::views::counted(raw_ptr, 0))
std::cout << i << ' ';
std::cout << '\n';
std::cout << "Exit\n";
}
Prints
1 2 3 4 5 6 7 8 9 10
1
empty for null pointer
Exit
Similarly std::views::subrange could be used with the (start, end] pointers. Check the library for more info.