Why am I getting an error converting a Foo** → Foo const**?

本文详细解释了在 C++ 中为什么不能将 Foo** 隐式转换为 Fooconst**,以及如何通过改变类型定义来避免潜在的运行时错误。通过具体的代码示例展示了不当转换可能引发的问题,并提出了安全的解决方案。

Because converting Foo**  Foo const** would be invalid and dangerous.

C++ allows the (safe) conversion Foo*  Foo const*, but gives an error if you try to implicitly convert Foo**  Foo const**.

The rationale for why that error is a good thing is given below. But first, here is the most common solution: simply change Foo const** to Foo const* const*:

class Foo { /* ... */ };

void f(Foo const** p);
void g(Foo const* const* p);

int main()
{
  Foo** p = /*...*/;
  ...
  f(p);  // ERROR: it's illegal and immoral to convert Foo** to Foo const**
  g(p);  // OK: it's legal and moral to convert Foo** to Foo const* const*
  ...
}
The reason the conversion from  Foo**    Foo const** is dangerous is that it would let you silently and accidentally modify a  const Foo object without a cast:
class Foo {
public:
  void modify();  // make some modification to the this object
};

int main()
{
  const Foo x;
  Foo* p;
  Foo const** q = &p;  // q now points to p; this is (fortunately!) an error
  *q = &x;             // p now points to x
  p->modify();         // Ouch: modifies a const Foo!!
  ...
}
If the  q = &p line were legal,  q would be pointing at  p. The next line,  *q = &x, changes  p itself (since  *q is  p) to point at  x. That would be a bad thing, since we would have lost the  const qualifier:  p is a Foo* but  x is a  const Foo. The  p->modify() line exploits  p's ability to modify its referent, which is the real problem, since we ended up modifying a  const Foo.

By way of analogy, if you hide a criminal under a lawful disguise, he can then exploit the trust given to that disguise. That's bad.

Thankfully C++ prevents you from doing this: the line q = &p is flagged by the C++ compiler as a compile-time error. Reminder: please do not pointer-cast your way around that compile-time error message. Just Say No!

(Note: there is a conceptual similarity between this and the prohibition against converting Derived** to Base**.)

From:http://www.parashift.com/c++-faq/constptrptr-conversion.html


It is correct that a double ** cannot be implicitly converted to a const double **. It can be converted to a const double * const *, though.

Imagine this scenario:

const double cd = 7.0;
double d = 4.0;
double *pd = &d;
double **ppd = &pd;
const double **ppCd = ppd;  //this is illegal, but if it were possible:
*ppCd = &cd;  //now *ppCd, which is also *ppd, which is pd, points to cd
*pd = 3.14; // pd now points to cd and thus modifies a const value!

So, if your function does not intend to modify any of the pointers involved, change it to take a const double * const *. If it intends to do modifications, you must decide whether all the modifications it does are safe and thus const_cast can be used, or whether you really need to pass in a const double **.

From: http://stackoverflow.com/questions/19910296/how-to-convert-pointer-to-pointer-type-to-const
// _GLIBCXX_RESOLVE_LIB_DEFECTS // DR 740 - omit specialization for array objects with a compile time length /** Specialization of default_delete for arrays, used by `unique_ptr<T[]>` * * @headerfile memory * @since C++11 */ template <typename _Tp> struct default_delete<_Tp[]> { public: /// Default constructor constexpr default_delete() noexcept = default; /** @brief Converting constructor. * * Allows conversion from a deleter for arrays of another type, such as * a const-qualified version of `_Tp`. * * Conversions from types derived from `_Tp` are not allowed because * it is undefined to `delete[]` an array of derived types through a * pointer to the base type. */ template <typename _Up, typename = _Require<is_convertible<_Up (*)[], _Tp (*)[]>>> _GLIBCXX23_CONSTEXPR default_delete(const default_delete<_Up[]> &amp;) noexcept {} /// Calls `delete[] __ptr` template <typename _Up> _GLIBCXX23_CONSTEXPR typename enable_if<is_convertible<_Up (*)[], _Tp (*)[]>::value>::type operator()(_Up *__ptr) const { static_assert(sizeof(_Tp) > 0, "can't delete pointer to incomplete type"); delete[] __ptr; } }; /// @cond undocumented // Manages the pointer and deleter of a unique_ptr template <typename _Tp, typename _Dp> class __uniq_ptr_impl { template <typename _Up, typename _Ep, typename = void> struct _Ptr { using type = _Up *; }; template <typename _Up, typename _Ep> struct _Ptr<_Up, _Ep, __void_t<typename remove_reference<_Ep>::type::pointer>> { using type = typename remove_reference<_Ep>::type::pointer; }; public: using _DeleterConstraint = enable_if< __and_<__not_<is_pointer<_Dp>>, is_default_constructible<_Dp>>::value>; using pointer = typename _Ptr<_Tp, _Dp>::type; static_assert(!is_rvalue_reference<_Dp>::value, "unique_ptr's deleter type must be a function object type" " or an lvalue reference type"); __uniq_ptr_impl() = default; _GLIBCXX23_CONSTEXPR __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; } template <typename _Del> _GLIBCXX23_CONSTEXPR __uniq_ptr_impl(pointer __p, _Del &amp;&amp;__d) : _M_t(__p, std::forward<_Del>(__d)) {} _GLIBCXX23_CONSTEXPR __uniq_ptr_impl(__uniq_ptr_impl &amp;&amp;__u) noexcept : _M_t(std::move(__u._M_t)) { __u._M_ptr() = nullptr; } _GLIBCXX23_CONSTEXPR __uniq_ptr_impl &amp;operator=(__uniq_ptr_impl &amp;&amp;__u) noexcept { reset(__u.release()); _M_deleter() = std::forward<_Dp>(__u._M_deleter()); return *this; } _GLIBCXX23_CONSTEXPR pointer &amp;_M_ptr() noexcept { return std::get<0>(_M_t); } _GLIBCXX23_CONSTEXPR pointer _M_ptr() const noexcept { return std::get<0>(_M_t); } _GLIBCXX23_CONSTEXPR _Dp &amp;_M_deleter() noexcept { return std::get<1>(_M_t); } _GLIBCXX23_CONSTEXPR const _Dp &amp;_M_deleter() const noexcept { return std::get<1>(_M_t); } _GLIBCXX23_CONSTEXPR void reset(pointer __p) noexcept { const pointer __old_p = _M_ptr(); _M_ptr() = __p; if (__old_p) _M_deleter()(__old_p); } _GLIBCXX23_CONSTEXPR pointer release() noexcept { pointer __p = _M_ptr(); _M_ptr() = nullptr; return __p; } _GLIBCXX23_CONSTEXPR void swap(__uniq_ptr_impl &amp;__rhs) noexcept { using std::swap; swap(this->_M_ptr(), __rhs._M_ptr()); swap(this->_M_deleter(), __rhs._M_deleter()); } private: tuple<pointer, _Dp> _M_t; }; // Defines move construction + assignment as either defaulted or deleted. template <typename _Tp, typename _Dp, bool = is_move_constructible<_Dp>::value, bool = is_move_assignable<_Dp>::value> struct __uniq_ptr_data : __uniq_ptr_impl<_Tp, _Dp> { using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl; __uniq_ptr_data(__uniq_ptr_data &amp;&amp;) = default; __uniq_ptr_data &amp;operator=(__uniq_ptr_data &amp;&amp;) = default; }; template <typename _Tp, typename _Dp> struct __uniq_ptr_data<_Tp, _Dp, true, false> : __uniq_ptr_impl<_Tp, _Dp> { using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl; __uniq_ptr_data(__uniq_ptr_data &amp;&amp;) = default; __uniq_ptr_data &amp;operator=(__uniq_ptr_data &amp;&amp;) = delete; }; template <typename _Tp, typename _Dp> struct __uniq_ptr_data<_Tp, _Dp, false, true> : __uniq_ptr_impl<_Tp, _Dp> { using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl; __uniq_ptr_data(__uniq_ptr_data &amp;&amp;) = delete; __uniq_ptr_data &amp;operator=(__uniq_ptr_data &amp;&amp;) = default; }; template <typename _Tp, typename _Dp> struct __uniq_ptr_data<_Tp, _Dp, false, false> : __uniq_ptr_impl<_Tp, _Dp> { using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl; __uniq_ptr_data(__uniq_ptr_data &amp;&amp;) = delete; __uniq_ptr_data &amp;operator=(__uniq_ptr_data &amp;&amp;) = delete; }; /// @endcond // 20.7.1.2 unique_ptr for single objects. /// A move-only smart pointer that manages unique ownership of a resource. /// @headerfile memory /// @since C++11 template <typename _Tp, typename _Dp = default_delete<_Tp>> class unique_ptr { template <typename _Up> using _DeleterConstraint = typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type; __uniq_ptr_data<_Tp, _Dp> _M_t; public: using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer; using element_type = _Tp; using deleter_type = _Dp; private: // helper template for detecting a safe conversion from another // unique_ptr template <typename _Up, typename _Ep> using __safe_conversion_up = __and_< is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>, __not_<is_array<_Up>>>; public: // Constructors. /// Default constructor, creates a unique_ptr that owns nothing. template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>> constexpr unique_ptr() noexcept : _M_t() { } /** Takes ownership of a pointer. * * @param __p A pointer to an object of @c element_type * * The deleter will be value-initialized. */ template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>> _GLIBCXX23_CONSTEXPR explicit unique_ptr(pointer __p) noexcept : _M_t(__p) { } /** Takes ownership of a pointer. * * @param __p A pointer to an object of @c element_type * @param __d A reference to a deleter. * * The deleter will be initialized with @p __d */ template <typename _Del = deleter_type, typename = _Require<is_copy_constructible<_Del>>> _GLIBCXX23_CONSTEXPR unique_ptr(pointer __p, const deleter_type &amp;__d) noexcept : _M_t(__p, __d) {} /** Takes ownership of a pointer. * * @param __p A pointer to an object of @c element_type * @param __d An rvalue reference to a (non-reference) deleter. * * The deleter will be initialized with @p std::move(__d) */ template <typename _Del = deleter_type, typename = _Require<is_move_constructible<_Del>>> _GLIBCXX23_CONSTEXPR unique_ptr(pointer __p, __enable_if_t<!is_lvalue_reference<_Del>::value, _Del &amp;&amp;> __d) noexcept : _M_t(__p, std::move(__d)) { } template <typename _Del = deleter_type, typename _DelUnref = typename remove_reference<_Del>::type> _GLIBCXX23_CONSTEXPR unique_ptr(pointer, __enable_if_t<is_lvalue_reference<_Del>::value, _DelUnref &amp;&amp;>) = delete; /// Creates a unique_ptr that owns nothing. template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>> constexpr unique_ptr(nullptr_t) noexcept : _M_t() { } // Move constructors. /// Move constructor. unique_ptr(unique_ptr &amp;&amp;) = default; /** @brief Converting constructor from another type * * Requires that the pointer owned by @p __u is convertible to the * type of pointer owned by this object, @p __u does not own an array, * and @p __u has a compatible deleter type. */ template <typename _Up, typename _Ep, typename = _Require<__safe_conversion_up<_Up, _Ep>, __conditional_t<is_reference<_Dp>::value, is_same<_Ep, _Dp>, is_convertible<_Ep, _Dp>>>> _GLIBCXX23_CONSTEXPR unique_ptr(unique_ptr<_Up, _Ep> &amp;&amp;__u) noexcept : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) { } #if _GLIBCXX_USE_DEPRECATED #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /// Converting constructor from @c auto_ptr template <typename _Up, typename = _Require< is_convertible<_Up *, _Tp *>, is_same<_Dp, default_delete<_Tp>>>> unique_ptr(auto_ptr<_Up> &amp;&amp;__u) noexcept; #pragma GCC diagnostic pop #endif /// Destructor, invokes the deleter if the stored pointer is not null. #if __cplusplus > 202002L &amp;&amp; __cpp_constexpr_dynamic_alloc constexpr #endif ~unique_ptr() noexcept { static_assert(__is_invocable<deleter_type &amp;, pointer>::value, "unique_ptr's deleter must be invocable with a pointer"); auto &amp;__ptr = _M_t._M_ptr(); if (__ptr != nullptr) get_deleter()(std::move(__ptr)); __ptr = pointer(); } // Assignment. /** @brief Move assignment operator. * * Invokes the deleter if this object owns a pointer. */ unique_ptr &amp;operator=(unique_ptr &amp;&amp;) = default; /** @brief Assignment from another type. * * @param __u The object to transfer ownership from, which owns a * convertible pointer to a non-array object. * * Invokes the deleter if this object owns a pointer. */ template <typename _Up, typename _Ep> _GLIBCXX23_CONSTEXPR typename enable_if<__and_< __safe_conversion_up<_Up, _Ep>, is_assignable<deleter_type &amp;, _Ep &amp;&amp;>>::value, unique_ptr &amp;>::type operator=(unique_ptr<_Up, _Ep> &amp;&amp;__u) noexcept { reset(__u.release()); get_deleter() = std::forward<_Ep>(__u.get_deleter()); return *this; } /// Reset the %unique_ptr to empty, invoking the deleter if necessary. _GLIBCXX23_CONSTEXPR unique_ptr &amp; operator=(nullptr_t) noexcept { reset(); return *this; } // Observers. /// Dereference the stored pointer. _GLIBCXX23_CONSTEXPR typename add_lvalue_reference<element_type>::type operator*() const noexcept(noexcept(*std::declval<pointer>())) { __glibcxx_assert(get() != pointer()); return *get(); } /// Return the stored pointer. _GLIBCXX23_CONSTEXPR pointer operator->() const noexcept { _GLIBCXX_DEBUG_PEDASSERT(get() != pointer()); return get(); } /// Return the stored pointer. _GLIBCXX23_CONSTEXPR pointer get() const noexcept { return _M_t._M_ptr(); } /// Return a reference to the stored deleter. _GLIBCXX23_CONSTEXPR deleter_type &amp; get_deleter() noexcept { return _M_t._M_deleter(); } /// Return a reference to the stored deleter. _GLIBCXX23_CONSTEXPR const deleter_type &amp; get_deleter() const noexcept { return _M_t._M_deleter(); } /// Return @c true if the stored pointer is not null. _GLIBCXX23_CONSTEXPR explicit operator bool() const noexcept { return get() == pointer() ? false : true; } // Modifiers. /// Release ownership of any stored pointer. _GLIBCXX23_CONSTEXPR pointer release() noexcept { return _M_t.release(); } /** @brief Replace the stored pointer. * * @param __p The new pointer to store. * * The deleter will be invoked if a pointer is already owned. */ _GLIBCXX23_CONSTEXPR void reset(pointer __p = pointer()) noexcept { static_assert(__is_invocable<deleter_type &amp;, pointer>::value, "unique_ptr's deleter must be invocable with a pointer"); _M_t.reset(std::move(__p)); } /// Exchange the pointer and deleter with another object. _GLIBCXX23_CONSTEXPR void swap(unique_ptr &amp;__u) noexcept { static_assert(__is_swappable<_Dp>::value, "deleter must be swappable"); _M_t.swap(__u._M_t); } // Disable copy from lvalue. unique_ptr(const unique_ptr &amp;) = delete; unique_ptr &amp;operator=(const unique_ptr &amp;) = delete; };
08-27
MATLAB主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性内容概要:本文主要介绍了一种在MATLAB环境下实现的主动噪声和振动控制算法,该算法针对较大的次级路径变化具有较强的鲁棒性。文中详细阐述了算法的设计原理与实现方法,重点解决了传统控制系统中因次级路径动态变化导致性能下降的问题。通过引入自适应机制和鲁棒控制策略,提升了系统在复杂环境下的稳定性和控制精度,适用于需要高精度噪声与振动抑制的实际工程场景。此外,文档还列举了多个MATLAB仿真实例及相关科研技术服务内容,涵盖信号处理、智能优化、机器学习等多个交叉领域。; 适合人群:具备一定MATLAB编程基础和控制系统理论知识的科研人员及工程技术人员,尤其适合从事噪声与振动控制、信号处理、自动化等相关领域的研究生和工程师。; 使用场景及目标:①应用于汽车、航空航天、精密仪器等对噪声和振动敏感的工业领域;②用于提升现有主动控制系统对参数变化的适应能力;③为相关科研项目提供算法验证与仿真平台支持; 阅读建议:建议读者结合提供的MATLAB代码进行仿真实验,深入理解算法在不同次级路径条件下的响应特性,并可通过调整控制参数进一步探究其鲁棒性边界。同时可参考文档中列出的相关技术案例拓展应用场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值