Compiler error when enforcing c++11 move semantics - c++11

I distilled the code and ended up with the one below. Error is when inserting into a map...
Thanks to the person who marked down the question - hopefully he will be the first to answer it ;).
Seriously now, my point is: the fact that move is done on a "best effort" basis and that one cannot ensure it is used (as here with a naive delete) makes room for a lot of confusion. To me it's dodgy one has to provide a never used copy constructor body just to silence the compiler.
Of course, may be a bad play between the compiler and my stl. Leaving the people who live and breathe the C++ standard to clarify.
#include <vector>
#include <map>
#include <iostream>
using namespace std;
struct FeedDataType
{
FeedDataType() {};
FeedDataType(FeedDataType&& ) = default;
#if 0
// error: use of deleted function 'FeedDataType::FeedDataType(const FeedDataType&) - why?
FeedDataType(const FeedDataType& ) = delete;
#else
// Compiles ok but never called. But cannot mark it delete...
FeedDataType(const FeedDataType& ) { cout << "Never called!" << endl; };
#endif
};
int main ()
{
vector< FeedDataType > x;
map<int, vector< FeedDataType > > mymap;
mymap.insert( std::make_pair( 0, x ) );
}
Error:
In file included from /cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/vector:63:0,
from play3.cpp:1:
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = FeedDataType; _Args = {const FeedDataType&}]':
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:77:3: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const FeedDataType*, std::vector<FeedDataType> >; _ForwardIterator = FeedDataType*; bool _TrivialValueTypes = false]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:119:41: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const FeedDataType*, std::vector<FeedDataType> >; _ForwardIterator = FeedDataType*]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:260:63: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const FeedDataType*, std::vector<FeedDataType> >; _ForwardIterator = FeedDataType*; _Tp = FeedDataType]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_vector.h:310:9: required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = FeedDataType; _Alloc = std::allocator<FeedDataType>; std::vector<_Tp, _Alloc> = std::vector<FeedDataType>]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_pair.h:137:64: required from 'constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U1 = int; _U2 = std::vector<FeedDataType>&; <template-parameter-2-3> = void; _T1 = int; _T2 = std::vector<FeedDataType>]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_pair.h:273:72: required from 'constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = std::vector<FeedDataType>&; typename std::__decay_and_strip<_T2>::__type = std::vector<FeedDataType>; typename std::__decay_and_strip<_T1>::__type = int]'
play3.cpp:24:44: required from here
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: use of deleted function 'FeedDataType::FeedDataType(const FeedDataType&)'
play3.cpp:13:9: error: declared here
ORIGINAL POST:
There is a compiler error (g++ 4.7.2 on RedHat) I'd like to clarify.
What is all about: simply want to enforce the use of the move constructor only.
So I write:
FeedDataType(FeedDataType&& ) = default;
FeedDataType(const FeedDataType& ) = delete;
Alas, it doesn't compile (I'll spare you the full stack trace):
.../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: use of deleted function 'FeedDataType::FeedDataType(const FeedDataType&)'
Not being obvious where copy ctor required, I write a body for it to let the program core when used and then look at the stack trace:
FeedDataType(FeedDataType&& ) = default;
FeedDataType(const FeedDataType& ) { cerr << "Called" << endl; assert(false); }
Well, the program compiles and runs without entering the copy ctor code. So, if not used, why cannot the copy ctor be deleted? How can one be sure of what is being used?
Other thing that I don't get:
void FeedData::add( const FeedDataType & feedDataRow )
{
// Is move used here? If not why not warn?
some_vector.push_back( std::move(feedDataRow) );
}
This apparently uses the copy constructor. Would have been good having a warning/error from the compiler that move construction was not in use here? In large programs it would be easy to miss the const and believe that std::move would do the job. In fact, should have been a compiler error - feedDataRow is const, move construction from it should not be possible?
Using non-const reference appears to be better as the move constructor is used in that case. Or should one pass here by value?
I think the first issue is a compiler bug, but would like to be enlightened by someone who knows what was going on. I was thinking move semantics is easy but obviously need to do my homework. Looking for Mr. Meyers books on C++11 ;).
Thanks,
Adrian

Well, the program compiles and runs without entering the copy ctor code.
So, if not used, why cannot the copy ctor be deleted?
How can one be sure of what is being used?
It may be in a scope where it may be use
in the following code:
void foo() = delete;
void bar(bool b)
{
if (b) { foo(); }
// Other stuff
}
Suppose that we call only bar(false), foo is not called, but bar use a deleted function anyway.
If you follow the error link, you will see in which part of code Copy Constructor may be used.

std::move means, approximately, "move if you can, otherwise copy" (not quite, but close enough for purposes of this discussion). It does not mean "move if you can, otherwise panic". The former semantics is useful most everywhere, including virtually all situations of writing template-based generic components. The latter is only useful in rare cases when moving might be impossible, and copying might be possible, but you don't want to copy.
The standard gives you the former semantics but not the latter. If you need the latter, you can write your own along these lines:
template< class T >
typename std::remove_reference<T>::type&& my_move( T&& t )
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
template< class T >
typename std::remove_reference<T>::type&& my_move( const T& t )
{
static_assert(!std::is_lvalue_reference<T&>::value, "Move is requested, but only copying is possible");
}

The copy constructor is used by the copy constructor of vector, which is used by the constructor of std::pair called in std::make_pair. The reason it's not actually called in a given run of your little example program is that the vector is empty.
Try std::move on the x when you call make_pair.

Related

Recursively unpacking a template pack for a parameter-less function

I'm trying to create a struct template with a variadic template type pack, that can deduct the sum of the size of all types passed in.
Below you find a simplified example, in the real-world context, the size computed is used to create further member objects.
template <typename... Types>
struct OverallSize
{
template <typename FirstType, typename... NextTypes>
static constexpr size_t sizesum() { return sizeof (FirstType) + sizesum<NextTypes...>(); }
template <typename LastType>
static constexpr size_t sizesum() { return sizeof (LastType); }
static constexpr size_t size = sizesum<Types...>();
};
// Should work e.g. like this
auto s = OverallSize<int, float, char>::size; // s will be 9 on x86-64
I'm used to this recursive parameter unpacking approach when it comes to argument lists and assumed this works as well with argument-less functions and explicit template specification. However I get the following error when compiling with clang
Call to 'sizesum' is ambiguous
...
Candidate function [with FirstType = unsigned long, NextTypes = <>]
Candidate function [with LastType = unsigned long]
So it seems as if the last recursion iteration doesn't work here – not sure why the compiler doesn't simply chose the most obvious choice: The one with only one template type – just as it would happen if there was an actual template argument passed to the function.
So, what do I have to do to make this compile and work as desired?
For C++14 you can use SFINAE:
template <
typename FirstType,
typename... NextTypes,
std::enable_if_t<sizeof...(NextTypes) >= 1>* = nullptr >
static constexpr size_t sizesum() {
return sizeof (FirstType) + sizesum<NextTypes...>();
}
this template will be considered only if parameters pack has size >= 1.
Demo

C++11: Variadic template deduction logic

I have the following construct:
template <class... Args>
class some_class
{
public:
some_class() = default;
some_class(Args...) = delete;
~some_class() = default;
};
template<>
class some_class<void>
{
public:
some_class() = default;
~some_class() = default;
};
The reason for this is that I just want to allow the users to create objects using the default constructor, so for example:
some_class<int,float> b;
should work but
some_class<int,float> c(1,3.4);
should give me a compilation error.
At some point in time I also needed to create templates based on void hence, the specialization for void:
some_class<void> a;
But by mistake I have typed:
some_class<> d;
And suddenly my code stopped compiling and it gave me the error:
some_class<Args>::some_class(Args ...) [with Args = {}]’ cannot be
overloaded
some_class(Args...) = delete;
So here comes the question: I feel that I am wrong that I assume that some_class<> should be deduced to the void specialization... I just don't know why. Can please someone explain why some_class<> (ie: empty argument list) is different from some_class<void>? (A few lines from the standard will do :) )
https://ideone.com/o6u0D6
void is a type like any other (an incomplete type, to be precise). This means it can be used as a template argument for type template parameters normally. Taking your class template, these are all perfectly valid, and distinct, instantiations:
some_class<void>
some_class<void, void>
some_class<void, void, void>
some_class<void, char, void>
In the first case, the parameter pack Args has one element: void. In the second case, it has two elements: void and void. And so on.
This is quite different from the case some_class<>, in which case the parameter pack has zero elements. You can easily demonstrate this using sizeof...:
template <class... Pack>
struct Sizer
{
static constexpr size_t size = sizeof...(Pack);
};
int main()
{
std::cout << Sizer<>::size << ' ' << Sizer<void>::size << ' ' << Sizer<void, void>::size << std::endl;
}
This will output:
0 1 2
[Live example]
I can't really think of a relevant part of the standard to quote. Perhaps this (C++11 [temp.variadic] 14.5.3/1):
A template parameter pack is a template parameter that accepts zero or more template arguments. [ Example:
template<class ... Types> struct Tuple { };
Tuple<> t0; // Types contains no arguments
Tuple<int> t1; // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> error; // error: 0 is not a type
—end example ]

std::map of non-movable objects [duplicate]

The following code will not compile on gcc 4.8.2.
The problem is that this code will attempt to copy construct an std::pair<int, A> which can't happen due to struct A missing copy and move constructors.
Is gcc failing here or am I missing something?
#include <map>
struct A
{
int bla;
A(int blub):bla(blub){}
A(A&&) = delete;
A(const A&) = delete;
A& operator=(A&&) = delete;
A& operator=(const A&) = delete;
};
int main()
{
std::map<int, A> map;
map.emplace(1, 2); // doesn't work
map.emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(2)
); // works like a charm
return 0;
}
As far as I can tell, the issue isn't caused by map::emplace, but by pair's constructors:
#include <map>
struct A
{
A(int) {}
A(A&&) = delete;
A(A const&) = delete;
};
int main()
{
std::pair<int, A> x(1, 4); // error
}
This code example doesn't compile, neither with coliru's g++4.8.1 nor with clang++3.5, which are both using libstdc++, as far as I can tell.
The issue is rooted in the fact that although we can construct
A t(4);
that is, std::is_constructible<A, int>::value == true, we cannot implicitly convert an int to an A [conv]/3
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed,
for some invented temporary variable t.
Note the copy-initialization (the =). This creates a temporary A and initializes t from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor of A, which makes the conversion ill-formed.
As we cannot convert from an int to an A, the constructor of pair that one would expect to be called is rejected by SFINAE. This behaviour is surprising, N4387 - Improving pair and tuple analyses and tries to improve the situation, by making the constructor explicit instead of rejecting it. N4387 has been voted into C++1z at the Lenexa meeting.
The following describes the C++11 rules.
The constructor I had expected to be called is described in [pairs.pair]/7-9
template<class U, class V> constexpr pair(U&& x, V&& y);
7 Requires: is_constructible<first_type, U&&>::value is true and
is_constructible<second_type, V&&>::value is true.
8 Effects: The
constructor initializes first with std::forward<U>(x) and second with
std::forward<V>(y).
9 Remarks: If U is not implicitly convertible to
first_type or V is not implicitly convertible to second_type this
constructor shall not participate in overload resolution.
Note the difference between is_constructible in the Requires section, and "is not implicitly convertible" in the Remarks section. The requirements are fulfilled to call this constructor, but it may not participate in overload resolution (= has to be rejected via SFINAE).
Therefore, overload resolution needs to select a "worse match", namely one whose second parameter is a A const&. A temporary is created from the int argument and bound to this reference, and the reference is used to initialize the pair data member (.second). The initialization tries to call the deleted copy ctor of A, and the construction of the pair is ill-formed.
libstdc++ has (as an extension) some nonstandard ctors. In the latest doxygen (and in 4.8.2), the constructor of pair that I had expected to be called (being surprised by the rules required by the Standard) is:
template<class _U1, class _U2,
class = typename enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>
>::value
>::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
and the one that is actually called is the non-standard:
// DR 811.
template<class _U1,
class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }
The program is ill-formed according to the Standard, it is not merely rejected by this non-standard ctor.
As a final remark, here's the specification of is_constructible and is_convertible.
is_constructible [meta.rel]/4
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:
T t(create<Args>()...);
[Note: These tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.
is_convertible [meta.unary.prop]/6:
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and
only if the return expression in the following code would be well-formed, including any implicit conversions
to the return type of the function:
To test() {
return create<From>();
}
[Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To and From. Only
the validity of the immediate context of the expression of the return-statement (including conversions to
the return type) is considered.
For your type A,
A t(create<int>());
is well-formed; however
A test() {
return create<int>();
}
creates a temporary of type A and tries to move that into the return-value (copy-initialization). That selects the deleted ctor A(A&&) and is therefore ill-formed.

C++11 class in std::map as Value with private constructors

Here is the simplified version of the class which is stored as value in a map which works fine in VS2008 (note that all members are private):
class Value{
friend class FriendClass;
friend class std::map<std::string, Value>;
friend struct std::pair<const std::string, Value>;
friend struct std::pair<std::string, Value>;
Value() {..}
Value(Value const& other) {..}
... rest members...
};
Code (called from FriendClass, so this can reach private constructors) :
FriendClass::func()
{
std::map<const std::string, Value> map;
map.insert(std::make_pair(std::string("x"), Value()));
}
This compiles w/o any error in VS2008, but fails on VS2015/C++11:
file.cpp(178): error C2664: 'std::_Tree_iterator>>> std::_Tree>::insert(std::_Tree_const_iterator>>>,const std::pair &)': cannot convert argument 1 from 'std::pair' to 'std::pair &&'
with
[
_Kty=std::string,
_Ty=Value,
_Pr=std::less,
_Alloc=std::allocator>
]
and
[
_Kty=std::string,
_Ty=Value
]
file.cpp(178): note: Reason: cannot convert from 'std::pair' to 'std::pair'
with
[
_Kty=std::string,
_Ty=Value
]
file.cpp(178): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
If I make the Value copy constructor public, it compiles fine in VS2015 as well. But that was private with purpose, and only made available for std::map and std::pair. However, it seems in C++11 additional friend access are also necessary to declare. Which are these?
Thank you.
I don't have access to the compilers you mentioned, but here's what I'm seeing on g++ 5.3.
Consider the following essentially-same version of your question:
#include <map>
#include <utility>
class foo
{
friend std::pair<const int, foo>;
foo(const foo &other){}
public:
foo(){}
};
int main()
{
using map_t = std::map<int, foo>;
map_t m;
m.insert(std::make_pair(2, foo()));
// m.emplace(2, foo());
}
(The default ctor is public, but that's non-essential and just makes the example shorter.)
In main, note the two lines
m.insert(std::make_pair(2, foo()));
// m.emplace(2, foo());
Reversing the comments builds fine, but the version shown doesn't:
/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, const _T2&) [with _U1 = int; <template-parameter-2-2> = void; _T1 = int; _T2 = foo]’:
/usr/include/c++/5/bits/stl_pair.h:281:72: required from ‘constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = foo; typename std::__decay_and_strip<_T2>::__type = foo; typename std::__decay_and_strip<_Tp>::__type = int]’
stuff.cpp:21:34: required from here
stuff.cpp:9:2: error: ‘foo::foo(const foo&)’ is private
foo(const foo &other){}
^
Looking at the source code std_pair.h shows that indeed it is trying to call the copy constructor. Unfortunately, you friended std::pair, but not std::make_pair.
The emplace version doesn't have this problem, but I suspect that this is implementation dependent. In general, if you want a container to store a completely opaque class, I would suggest that you use a container of std::shared_ptrs to them. This allows you to completely specify which function/class can create/copy objects in your own code, and makes no assumptions on the library's code.

decltype(*this) bug in VS2013?

While trying to formulate a C macro to ease the writing of non-const member functions calling const member functions with exact same logic (see Chapter 1, Item 3, "Avoiding Duplication in const and Non-const Member Functions" in Effective C++), I believe I came across a decltype() bug in VS2013 Update 1.
I wanted to use decltype(*this) to build a static_cast<decltype(*this) const&>(*this) expression in the aforementioned macro to avoid having the macro call site pass any explicit type information. However, that latter expression doesn't appear to properly add const in some cases in VS2013.
Here's a small block of code I was able to make repo the bug:
#include <stdio.h>
template<typename DatumT>
struct DynamicArray
{
DatumT* elements;
unsigned element_size;
int count;
inline const DatumT* operator [](int index) const
{
if (index < 0 || index >= count)
return nullptr;
return &elements[index];
}
inline DatumT* operator [](int index)
{
#if defined(MAKE_THIS_CODE_WORK)
DynamicArray const& _this = static_cast<decltype(*this) const&>(*this);
return const_cast<DatumT*>(_this[index]);
#else
// warning C4717: 'DynamicArray<int>::operator[]' : recursive on all control paths, function will cause runtime stack overflow
return const_cast<DatumT*>(
static_cast<decltype(*this) const>(*this)
[index]
);
#endif
}
};
int _tmain(int argc, _TCHAR* argv[])
{
DynamicArray<int> array = { new int[5], sizeof(int), 5 };
printf_s("%d", *array[0]);
delete array.elements;
return 0;
}
(may the first one to blab about not using std::vector be smitten)
You can either compile the above code and see the warning yourself, or refer to my lone comment to see what VC++ would spew at you. You can then ! the defined(MAKE_THIS_CODE_WORK) expression to have VC++ compile the code as how I'm excepting the #else code to work.
I don't have my trusty clang setup on this machine, but I was able to use GCC Explorer to see if clang complains (click to see/compile code). Which it doesn't. However, g++ 4.8 will give you an ‘const’ qualifiers cannot be applied to ‘DynamicArray&’ error message using that same code. So perhaps g++ also has a bug?
Referring to the decltype and auto standards paper (albeit, it's almost 11 years old), the very bottom of page 6 says that decltype(*this) in a non-const member function should be T&, so I'm pretty sure this should be legal...
So am I wrong in trying to use decltype() on *this plus adding const to it? Or is this a bug in VS2013? And apparently g++ 4.8, but in a different manner.
edit: Thanks to Ben Voigt's response I was able to figure out how to craft a standalone C macro for what I'm desire to do.
// Cast [this] to a 'const this&' so that a const member function can be invoked
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...) \
const_cast<ret_type>( \
static_cast< \
std::add_reference< \
std::add_const< \
std::remove_reference< \
decltype(*this) \
>::type \
>::type \
>::type \
>(*this) \
__VA_ARGS__ \
)
// We can now implement that operator[] like so:
return CAST_THIS_NONCONST_MEMBER_FUNC(DatumT*, [index]);
The original desire was to hide this all in a macro, which is why I wasn't wanting to worry about creating typedefs or this aliases. It is still curious that clang in GCC Explorer didn't output a warning...though the output assembly does appear fishy.
You said yourself, decltype (*this) is T&. decltype (*this) const & tries to form a reference to a reference (T& const &). decltype triggers the reference collapsing rule 8.3.2p6. But it doesn't collapse the way you'd like.
You could say decltype(this) const&, but that would be T* const& -- a reference to a const pointer, not a pointer to a const object. For the same reason, decltype (*this) const and const decltype (*this) don't form const T&, but (T&) const. And top-level const on a reference is useless, since references already forbid rebinding.
Perhaps you are looking for something more like
const typename remove_reference<decltype(*this)>::type &
But note that you don't need the cast at all when adding const. Instead of
DynamicArray const& _this = static_cast<decltype(*this) const&>(*this);
just say
DynamicArray const& _this = *this;
These combine to
const typename std::remove_reference<decltype(*this)>::type & this_ = *this;
Still, this is a stupid amount of code for a very simple and pervasive problem. Just say:
const auto& this_ = *this;
FYI here's the text of the reference collapsing rule:
If a typedef-name (7.1.3, 14.1) or a decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a type T, an attempt to create the type "lvalue reference to cv TR" creates the type "lvalue reference to T", while an attempt to create the type "rvalue reference to cv TR" creates the type TR.
decltype(*this) is our decltype-specifier which denotes TR, which is DynamicArray<DatumT>&. Here, T is DynamicArray<DatumT>. The attempt TR const& is the first case, attempt to create lvalue reference to (const) TR, and therefore the final result is T&, not const T&. The cv-qualification is outside the innermost reference.
With regard to your macro
// Cast [this] to a 'const this&' so that a const member function can be invoked
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...) \
const_cast<ret_type>( \
static_cast< \
std::add_reference< \
std::add_const< \
std::remove_reference< \
decltype(*this) \
>::type \
>::type \
>::type \
>(*this) \
__VA_ARGS__ \
)
It's much cleaner to do
// Cast [this] to a 'const this&' so that a const member function can be invoked
template<typename T> const T& deref_as_const(T* that) { return *that; }
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...) \
const_cast<ret_type>(deref_as_const(this)__VA_ARGS__)
It's shorter, self-contained, compatible with C++98 except for __VA_ARGS__, and avoids an unnecessary cast

Resources