【问题标题】:Cast between pointer-to-member and pointer-to-const-member在指向成员的指针和指向常量成员的指针之间转换
【发布时间】:2013-11-16 05:27:26
【问题描述】:

我试过搜索这个,但我想到的每个词最终都会得到完全不相关的结果。

我有一个将指向成员的指针作为参数的函数(模板),但我似乎无法隐含地将指向的成员视为 const。使用 const_cast 是可行的,但如果可以的话,我想避免显式调用它。

struct MyStruct
{
  int *_array;
  int _size;
};

template<typename C, typename T>
void DoSomething(T* C::* arr, int siz)
{
  // do some read-only stuff with the member here
}

template<typename C, typename T>
void ConstDoSomething(T* C::* arr, int siz)
{
  DoSomething<C, T const>(arr, siz);
  // DoSomething<C, T const>(const_cast<T const* C::*>(arr), siz); // works
}

MyStruct ms;
ConstDoSomething<MyStruct const, int>(&MyStruct::_array, ms._size); // note:   cannot convert ‘arr’ (type ‘int* MyStruct::*’) to type ‘const int* MyStruct::*’

这是一个简化的示例,演示了我在使用更复杂的类树时遇到的问题。我试图避免强制转换,因为调用代码(例如,使用类模板的人)需要它。


更新:当我第一次发布这个时,我不小心使用了一个没有产生相同错误的代码示例。我花了相当多的测试来确定根本原因,即我在模板参数中添加了 const 限定符。上面的示例现在正确地演示了我正在使用的行为。

【问题讨论】:

  • 不管怎样,我在 llvm 5.0、mingw gcc 4.7.0 和 4.7.2 上成功编译了你的代码片段。您能否更新您的 mingw 安装,或者在帖子中包含一个完整的程序?
  • 我不知道我昨晚在做什么,我认为这个 sn-p 给出了相同的结果。 一切我试图复制它,但没有使用我完整的 550+ 行程序编译正常。我显然在其他地方犯了一些愚蠢的错误。如果我找不到它,那么我想我会在这里发布完整的代码。 ://
  • 代码示例已更新。如果您有任何其他详细信息,请告知,谢谢!

标签: c++ compiler-errors constants pointer-to-member


【解决方案1】:

你使用什么编译器?我已经尝试过您的代码 Visual Studio 2012 和 2013,它的编译没有任何警告或错误。 无论哪种方式 - 当你玩 constness 时,你应该尝试 const_cast 而不是 static_cast

【讨论】:

  • 我实际上在其他地方读到过,在添加 const 时,static_cast 和 const_cast 应该工作相同,并且 const_cast 仅用于删除 cv 限定符。与此相反,使用 const_cast 可以编译。在我的 mingw 文件夹中翻找似乎表明它是 4.7.1。我使用 Code::Blocks 12.11 作为我的 IDE,并使用了与之捆绑的 mingw 包。
  • 另外,我要补充一点,认为这个 sn-p 产生相同的结果是我的错误。我显然在某处弄脏了别的东西。我将不得不尝试追踪它。谢谢!
  • 我更新了代码示例,它现在准确地反映了错误(使用 gcc 4.7)。
【解决方案2】:

为了代替更好的解决方案(至少现在是这样),我创建了一个单独的重载来接受指向非 const 成员的指针,然后使用 const_cast 来调用原始方法。

如前所述,上面的示例是为了简单和清晰起见,但实际上我使用了几个类模板,每个模板都继承自另一个模板,依此类推。这导致了以下相当丑陋的解决方案:

// C = containing class (for pointer-to-members, std::nullptr_t if none)
// T = type of data being referenced

template<typename _C, typename _T> struct MakeMemberPointer // STL doesn't provide this??
{
public:
  typedef _T _C::* Type;
};

// Note: base-class template has specialization for C == std::nullptr_t
typedef typename std::conditional<std::is_same<C, decltype(nullptr)>::value,
  T const* C::*,
  T const*
>::type N;

typedef typename std::conditional<std::is_member_pointer<T>::value,
  typename MakeMemberPointer<
    C,
    typename std::add_pointer<
      typename std::remove_const<
        typename std::remove_reference<
          decltype(*(static_cast<C*>(nullptr)->*static_cast<N>(nullptr))) // T const&
        >::type // reference removed -> T const
      >::type // const removed -> T
    >::type // pointer added -> T*
  >::Type, // member pointer -> T* C::*
  typename std::add_pointer<typename std::remove_const<typename std::remove_pointer<N>::type>::type>::type
>::type NNoConst;

void DoSomething(N t) noexcept
{
}

void DoSomething(NNoConst t) noexcept
{
  DoSomething(const_cast<N>(t));
}

包含所有这些的类是从非 const 基类派生的仅 const 类(但由于模板参数,此处与 const 成员一起使用)。在类中声明所有这些(对我来说)比在调用代码中使用 const_cast 更可取,但我仍然不明白为什么 gcc 不允许这种转换隐式(毕竟,它只是 adding const 限定符!!)。

【讨论】:

  • 实际上,在进一步反思我正在做的事情之后......在类模板树中将 const 抽象出来并为非常量提供单独的 typedef 确实要简单得多.
猜你喜欢
  • 1970-01-01
  • 2018-03-24
  • 1970-01-01
  • 2013-05-02
  • 2023-03-12
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多