Reputation: 10437
I have a struct that manages 'views' into an array of variable type. The purpose of this is to provide a unified state vector for ODE simulation while at the same time working on individual segments of this vector from several other classes in an organized fashion. If this triggers a design pattern in your mind, please let me know.
My issue is that the first implementation ContainerHolderFirst
, using Cont::pointer
does not compile for const arrays.
My next attempt with std::conditional
, mixing in Cont::const_pointer
still doesn't work.
Only the third attempt with std::conditional
and modification of Cont::value_type
compiles (and seems to work in my larger project).
My questions are the following:
ContainerHolderFirst
would work. I'd wish that const-ness of the type would be propagated to the pointer. Why isn't it?ContainerHolderSecond
doesn't work. The explanation in https://stackoverflow.com/a/1647394/1707931 rather suggests that this is the way to go, no?Full C++11 code follows:
Update1: Fixing ContainerHolderSecond
. It does compile with correct initialization. Also added ContainerHolderBarry suggested by Barry using decltype
and declval
.
This leaves the question whether any of the approaches are preferred? Will they lead to performance differences? They should all compile to the same object, no?
#include <iostream>
#include <array>
template <typename Cont>
class ContainerHolderFirst {
Cont& data_;
const static size_t offset_ = 1;
typename Cont::pointer data_view;
public:
ContainerHolderFirst(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderSecond {
using Pointer = typename std::conditional<std::is_const<Cont>::value,
typename Cont::const_pointer,
typename Cont::pointer>::type;
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderSecond(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderBarry {
using Pointer = decltype(&std::declval<Cont&>()[0]);
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderBarry(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
int main() {
using namespace std;
array<int, 2> my_array;
ContainerHolderFirst<array<int, 2>> holder(my_array); // works
const array<int, 2> const_array{5,7};
// ContainerHolderFirst<const array<int, 2>> const_holder(const_array);
/* error: invalid conversion from 'const value_type* {aka const int*}' to 'std::array<int, 2ull>::pointer {aka int*}' [-fpermissive] */
ContainerHolderSecond<array<int,2>> second_holder(my_array); // works!
ContainerHolderSecond<const array<int,2>> const_holder(const_array); //updated; works as well; awkward
ContainerHolderThird<array<int,2>> third_holder(my_array); // still works
ContainerHolderThird<const array<int,2>> third_const_holder(const_array); //finally compiles as well
ContainerHolderBarry<array<int,2>> barry_holder(my_array);
ContainerHolderBarry<const array<int,2>> barry_const_holder(const_array);
}
Upvotes: 1
Views: 145
Reputation: 157344
The only problem with ContainerHolderSecond
is that you're using it incorrectly:
ContainerHolderSecond<array<int,2>> const_holder(const_array);
// ^-- insert "const" here
As for ContainerHolderFirst
, the reason that array<T, N>::pointer
is the same type as (array<T, N> const)::pointer
is that there is no automatic way to determine where the const
qualification should be added to the nested type, and there is no language facility to describe this (that is, we don't have const
-qualified typedefs or type aliases).
Upvotes: 1
Reputation: 302827
You're making this unnecessarily difficult on yourself. If you want the type of &cont[offset]
, just ask for the type of that expression. Use std::declval
along with decltype
:
template <typename Cont>
class ContainerHolder {
using Pointer = decltype(&std::declval<Cont&>()[0]);
...
};
Upvotes: 1