Given the following code:
#include <iostream>
#include <type_traits>
int main() {
std::aligned_storage<sizeof(double), alignof(double)> storage;
std::aligned_union<sizeof(double), double> union_storage;
std::cout << sizeof(storage) << '\n';
std::cout << sizeof(union_storage) << '\n';
std::cout << sizeof(double) << '\n';
}
I expect sizeof(storage) and sizeof(union_storage) to be greater or equal to sizeof(double) since they have to be able to hold a double. However, I get the output
1
1
8
clang-3.8 and gcc-5.3 both produce this output.
Why does sizeof return an incorrect size?
If I use placement new to put a double into storage or union_storage would that be undefined behavior?
std::aligned_storage and std::aligned_union are type traits which provide a member type which is the actual type of the storage.
Thus, placing a double in the memory of the actual trait type would indeed be UB because they're empty types with just a typedef member.
#include <iostream>
#include <type_traits>
int main()
{
using storage_type =
std::aligned_storage<sizeof(double), alignof(double)>::type;
using union_storage_type =
std::aligned_union<sizeof(double), double>::type;
storage_type storage;
union_storage_type union_storage;
std::cout << sizeof(storage_type) << '\n';
std::cout << sizeof(union_storage_type) << '\n';
std::cout << sizeof(storage) << '\n';
std::cout << sizeof(union_storage) << '\n';
std::cout << sizeof(double) << '\n';
return 0;
}
This gives:
8
8
8
8
8
Note: As #T.C. correctly noted: C++14 provides alias templates ending on _t for the std type traits (i.e. std::aligned_storage<L, A>::type === std::aligned_storage_t<L,A>). The benefit is
No typename in template dependant context.
Less typing. ;)
Related
std::array has a built-in method empty() to check if the array is empty. As shown in the example copied from here:
#include <array>
#include <iostream>
int main()
{
std::array<int, 4> numbers {3, 1, 4, 1};
std::array<int, 0> no_numbers;
std::cout << std::boolalpha;
std::cout << "numbers.empty(): " << numbers.empty() << '\n';
std::cout << "no_numbers.empty(): " << no_numbers.empty() << '\n';
}
Are there any ways to check if the array is declared, with some fixed size, but not explicitly initialized?
Say, something like this?
std::array<int,4> a;
std::array<int,4> b;
a = {1,2,3,4}; //a holds some explicit values
//do not assign values to b
//how to tell the different state of a and b?
No. std::array<T, N>::empty() where N!=0 will always return false. https://en.cppreference.com/w/cpp/container/array/empty "Checks if the container has no elements, i.e. whether begin() == end()."
Similarly std::array<T, 0>::empty() will always return true, because begin() == end().
The size of a std::array object (that is, the number of elements it has) must be a compile-time constant, and cannot be changed dynamically. Thus, the empty() and size() member functions would perhaps seem somewhat redundant†(but included for reasons of compatibility with generic, container-based algorithms provided by the STL).
If you need a container with a size that can be changed, then you should use std::vector instead of std::array (although this has more run-time overheads).
The following illustrates one of the differences:
#include <iostream>
#include <array>
#include <vector>
int main()
{
std::array<int, 4> a;
std::array<int, 4> b;
a = { 1,2,3,4 }; //a holds some explicit values
std::cout << std::boolalpha;
std::cout << "a.empty(): " << a.empty() << '\n'; // false
std::cout << "b.empty(): " << b.empty() << '\n'; // false
// However ...
std::vector<int> v1;
std::vector<int> v2;
v1 = { 1,2,3,4 };
std::cout << "v1.empty(): " << v1.empty() << '\n'; // false
std::cout << "v2.empty(): " << v2.empty() << '\n'; // true
return 0;
}
†But note, those members are useful for cases when an array is passed to a function, which otherwise would not be able to determine the size (or emptiness) of its given argument(s).
I don't understand what you're asking for:
Are there any ways to check if the array is declared, with some fixed size, but not explicitly initialized?
If you have a variable of type std::array, then it has been constructed.
If it's been constructed, then the elements in the array have been constructed.
(that's done in the array constructor)
This variable std::array<std::string, 5> sa; contains 5 default-constructed strings.
So I don't know what you mean by "declared, but not explicitly initialized"
I should start by saying that my knowledge of C++ is pretty limited. I have some understanding of templates and specialization, but I'm by no means an experienced C++ programmer. For example I've today learnt about "aliases" which are not quite the same as "typedefs" and this is completely news to me.
I've been reading up a bit on alias template functions, but I have to admit that I find most examples very cryptic, so I've come up with a very simple use case, see below.
#include <iostream>
// A fictitious 24-bit type, that can be manipulated like any other type but can be distinguished from the underlying 32-bit type
using int24_t = int32_t;
template<typename T>
void foo(T x)
{
std::cout << "x = " << x << std::endl;
}
template<>
void foo<int24_t>(int24_t x)
{
std::cout << "24-bit specialization - x = " << x << std::endl;
}
int main(void)
{
foo<int16_t>(0);
foo<int24_t>(1);
foo<int32_t>(2); // Indistinguishable from 24-bit
}
Is it possible to do what I want, i.e. have a specialization of foo<int24_t> but also have a general purpose implementation of foo<int32_t> ?
When you've made an alias (using typedef or using) that alias is indistinguishable from the original type. You could consider making int24_t an enum though.
Example:
#include <cstdint>
#include <iostream>
enum int24_t : std::int32_t {};
template<typename T>
void foo(T v) {
std::cout << "base " << v << '\n';
}
template<>
void foo<std::int32_t>(std::int32_t v) {
std::cout << "int32_t " << v << '\n';
}
template<>
void foo<int24_t>(int24_t v) {
std::cout << "int24_t " << v << '\n';
}
int main() {
int24_t a{1};
std::int32_t b{2};
unsigned c{3};
foo(a);
foo(b);
foo(c);
a = static_cast<int24_t>(b); // needed to assign an int32_t to the enum type
foo(a);
}
Output
int24_t 1
int32_t 2
base 3
int24_t 2
#include <iostream>
template<typename T = char>
T cast(T in){
return in;
}
int main(){
std::cout << cast<>(5) << std::endl;
return 0;
}
Above will print 5 instead of an empty character, as the function is supposed to return a character by default and not an int. What am I ding wrong?
Edit:
Forcing it with std::cout << cast<char>(5) << std::endl; shows an empty character.
The declaration of 5 is an integer by default. This causes 'T' to be overridden with the type int, rather than using your default type. If you really wanted the char of value 5 (which you probably don't), you could specify it as '\x5'.
For the ascii character 5....
int main(){
std::cout << cast('5') << std::endl;
return 0;
}
Default types in templates tend to be useful when it's not easy to determine the template type, e.g. cast from int
template<typename T = char>
T cast(int v){
return T(v);
}
and now this will default to a method that casts an int to a char (rather than a int to int).
std::cout << cast(53) << std::endl;
Does the std::vector in C++ compact bools? I mean I have read that std::vector can combine 8 booleans into 1 byte. However, when I tried this code in visual studio,
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<bool> array {true, false, false, true, true,false,false,true};
cout << sizeof(array) << endl;
cout << sizeof(array[0]) << endl;
getchar();
return 0;
}
it printed:
24
16
while in another IDE, such as codeblocks, it printed 20 and 8.
I don't quite get what it does with booleans here.
Does the std::vector in C++ compact bools?
Yes, it is allowed to do so, and typically does.
I don't quite get what it does with booleans here.
You actually don't get what array[0] evaluates to.
It does not evaluate to a bit. It evaluates to a proxy object that correctly handles both conversion to bool and assignment from bool.
the sizeof this proxy does not have much significance. It is not the size of a bit or a bool. It's the size of an object programmed to act on a specific bit.
std::vector usually uses dynamic allocation internally by default. If you define your own allocator that tracks actual allocation size, you'll see that the number of bytes allocated for vector<bool> implies values are stored as bits:
#include <vector>
#include <iostream>
template<typename T>
class my_allocator : public std::allocator<T> {
public:
T * allocate(const size_t count) {
std::cout << "Allocated " << count << " * " << typeid(T).name() << std::endl;
std::cout << "Total size: " << count * sizeof(T) << std::endl;
return std::allocator<T>::allocate(count);
}
T * allocate(const size_t count, const void *) {
return allocate(count);
}
template<typename U>
struct rebind {
typedef my_allocator<U> other;
};
my_allocator() noexcept {};
my_allocator(const my_allocator<T>&) noexcept = default;
template<typename Other>
my_allocator(const my_allocator<Other>&) noexcept {}
};
int main() {
std::vector<int, my_allocator<int>> v1 { 0 };
std::vector<bool, my_allocator<bool>> v2 { 0 };
v1.reserve(100);
v2.reserve(100);
return 0;
}
Relevant output:
Allocated 100 * int
Total size: 400
Allocated 4 * unsigned int
Total size: 16
Demo: https://wandbox.org/permlink/WHTD0k3sMvd3E4ag
Is it possible to convert foo from float to long (and vice versa)?
auto foo = float(1234567891234.1234);
cout << "foo: " << foo << endl;
foo = long(1234567891234.1234);
cout << "foo: " << foo << endl;
The output is always:
foo: 1.23457e+12
foo: 1.23457e+12
Not in the way you wrote it. First,
auto foo = float(1234567891234.1234);
uses auto type deduction rules to infer the type of the RHS, and the result is float. Once this is done, the type of foo is float and it is set in stone (C++ is statically typed, unlike e.g. Python). When you next write
foo = long(1234567891234.1234);
the type of foo is still float and it is not magically changed to long.
If you want to emulate a "change" of type you can at most perform a cast:
cout << "foo (as long): " << static_cast<long>(foo) << endl;
or use an additional variable
long foo_long = foo; // again you may have a loss of precision
but be aware of possible precision loss due to floating point representation.
If you have access to a C++17 compiler, you can use an std::variant<long, float>, which is a type-safe union, to switch between types. If not, you can just use a plain old union like
#include <iostream>
union Foo
{
float f;
long l;
};
int main()
{
Foo foo;
foo.f = float(1234567891234.1234); // we set up the float member
std::cout << "foo: " << foo.f << std::endl;
foo.l = long(1234567891234.1234); // we set up the long member
std::cout << "foo: " << foo.l << std::endl;
}
Live on Coliru
Or, you can use a type-erasure technique like
#include <iostream>
int main()
{
void *foo; // we will store the object via this pointer
foo = new int{42};
std::cout << *(int*)foo << '\n';
operator delete(foo); // don't do delete foo, it is undefined behaviour
foo = new float{42.42};
std::cout << *(float*)foo << '\n';
operator delete(foo); // don't do delete foo, it is undefined behaviour
}
Live on Coliru
The modern version of the code above can be re-written with a std::shared_ptr like
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<void> foo{new int{42}};
std::cout << *(int*)foo.get() << '\n';
foo.reset(new float{42.42});
std::cout << *(float*)foo.get() << '\n';
}
Live on Coliru
A std::unique_ptr<void> won't work as only std::shared_ptr implements type-erasure.
Of course, if you don't really care about storage size etc, just use 2 separate variables.