I've just run into curious std::experimental::propagate_const bug. The following snippet demonstrates the problem
#include <memory>
#include <experimental/propagate_const>
#include <map>
class FWD;
//compiles
class A
{
std::unique_ptr<FWD> m;
};
//compiles
class B
{
std::experimental::propagate_const<std::unique_ptr<FWD>> m;
};
//compiles
class C
{
std::unique_ptr<std::map<int, FWD>> m;
};
//does not compile!
class D
{
std::experimental::propagate_const<std::unique_ptr<std::map<int, FWD>>> m;
};
So you can't just replace unique_ptr with propagating unique_ptr because sometimes your forward declarations will break it.
I would appreciate if someone would explain to me why compilation fails in current implementation of propagate_const. It has something to do with
typedef remove_reference_t<decltype(*std::declval<_Tp&>())> element_type;
Because workaround is:
template <typename T, typename = void>
struct get_element_type
{
using type = std::remove_reference_t<decltype(*std::declval<T&>())>;
};
template <typename T>
struct get_element_type<T, typename std::enable_if<!std::is_void<typename T::element_type>::value>::type>
{
using type = typename T::element_type;
};
// Namespaces and class declaration...
using element_type = typename get_element_type<T>::type;
Tested compilers: clang, gcc.
P.S. I wonder whether compiler devs know about it or not.
Instantiating a standard library template with an incomplete type is generally prohibited.
std::map
is not an exception to that rule.
Querying decltype(*std::declval<_Tp&>())
with _Tp = std::unique_ptr<std::map<int, FWD>>
necessitates the instantiation of all associated classes of _Tp
to look for potential friend operator*
declarations.
Among those associated classes is std::map<int, FWD>
.
Instantiation of std::map<int, FWD>
invokes undefined behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With