试图为 MFC 的 CMap 编写一个 for_each 算法

Posted

技术标签:

【中文标题】试图为 MFC 的 CMap 编写一个 for_each 算法【英文标题】:Trying to write a for_each algorithm for MFC's CMap 【发布时间】:2015-02-10 16:58:22 【问题描述】:

是的,我知道我应该创建一个迭代器,但是我需要快速完成,并且为任何与 VC++ 相关的东西编写一个合适的迭代器是令人沮丧的。 (这也适用于许多其他标准的东西,并且会增加我的工作量。:()

所以我写了一个 for_each() 算法来处理这个问题:

template <typename K, typename AK, typename V, typename AV>
void for_each(CMap<K, AK, V, AV>& container, std::function<void(AK, AV)> body)

    POSITION pos = container.GetStartPosition();
    while (pos != NULL)
    
        AK key;
        AV value;
        // Get key and value
        container .GetNextAssoc(pos, key, value);
        body(key, value);
    

但是显然VC++不能为函数模板推导出AK和AV。这是正常现象还是 VC++ 的另一个限制?

编辑 这是所要求的错误:

1>d:\projects\clean\cv32\cvcustombuttoninfo.cpp(113): error C2784: 'void for_each(CMap<KEY,ARG_KEY,VALUE,ARG_VALUE> &,std::function<void(AK,AV)>)' : could not deduce template argument for 'std::function<void(AK,AV)>' from 'CCVCustomRibbonInfo::WriteFile::<lambda_0513c2955d2b7b0197efcf2b0ce9322b>'
1>          d:\projects\clean\cv32\cvcustombuttoninfo.cpp(66) : see declaration of 'for_each'

这似乎也发生在带有 -std=c++11 参数的 gcc 4.9.0 上。

#include <functional>
template <typename T>
void fn(T t, std::function<void(T)>)



int main()

  int i;
  fn(i, [](int i));
  return 0;

[DEMO]

还有 g++ 错误:

/tmp/gcc-explorer-compiler115110-34-1cr9oud/example.cpp: In function 'int main()':
11 : error: no matching function for call to 'fn(int&, main()::)'
fn(i, [](int i));
^
11 : note: candidate is:
4 : note: template void fn(T, std::function)
void fn(T t, std::function<void(T)>)
^
4 : note: template argument deduction/substitution failed:
11 : note: 'main()::' is not derived from 'std::function'
fn(i, [](int i));
^
Compilation failed

【问题讨论】:

这很奇怪。你怎么称呼它?另外,请添加编译器错误。 有趣,如果我用另一个模板参数 T 替换 std::function&lt;void(AK, AV)&gt; 规范,它会编译。 【参考方案1】:

C++ lambda 与具有相同签名的 std::function 的类型不同。这就是模板参数推导失败的原因——它或多或少适用于确切的类型。使用template argument deduction 不考虑转换。您不想在模板参数中使用std::function。要强制签名,您可以检查Func 是否可转换为std::function&lt;void(AK, AV)&gt;

template <typename K, typename AK, typename V, typename AV, typename Func, typename = std::enable_if_t<std::is_convertible<Func, std::function<void(AK, AV)>>::value> >
void for_each(CMap<K, AK, V, AV>& container, Func body)

    POSITION pos = container.GetStartPosition();
    while (pos != NULL)
    
        AK key;
        AV value;
        // Get key and value
        container .GetNextAssoc(pos, key, value);
        body(key, value);
    

【讨论】:

是的,我刚刚想通了。如何强制 Func 类型具有特定签名? 好吧,如果声明了错误的类型参数,它看起来会报告一个足够好的错误消息。但是,如果 CMap&lt;int, int, int, int&gt; 带有带有参数 int&amp;, int&amp; 的 lambda 呢?我真的应该不允许这样做。【参考方案2】:

你应该创建一个迭代器:

#include <iterator>

// std::is_const
template <class T>
struct is_const  static const bool value = false; ;
template <class T>
struct is_const<const T>  static const bool value = true; ;

// std::conditional_t<C, std::add_const_t<T>, T>
template <bool C, class T>
struct maybe_const  typedef const T type; ;
template <class T>
struct maybe_const<false, T> 
  typedef T type;
;

template <class CMapType>
class cmap_iterator_base 
public:
  typedef std::forward_iterator_tag iterator_category;
  typedef typename CMapType::CPair value_type;
  typedef typename maybe_const<
    is_const<CMapType>::value, value_type
  >::type cv_value_type;
  typedef cv_value_type& reference;
  typedef cv_value_type* pointer;

  cmap_iterator_base() : cmap_(0), pos_(0) 
  cmap_iterator_base(CMapType& cmap) : cmap_(&cmap), pos_(cmap.PGetFirstAssoc()) 

  bool operator==(const cmap_iterator_base& other) const 
    return pos_ == other.pos_;
  
  bool operator!=(const cmap_iterator_base& other) const 
    return !(*this == other);
  

  cmap_iterator_base& operator++() 
    pos_ = cmap_->PGetNextAssoc(pos_);
    return *this;
  
  cmap_iterator_base operator++(int) 
    cmap_iterator_base tmp = *this;
    ++*this;
    return tmp;
  

  reference operator*() const 
    return *pos_;
  

  pointer operator->() const 
    return pos_;
  

private:
  CMapType* cmap_;
  pointer pos_;
;

template <class K, class AK, class V, class AV>
cmap_iterator_base<CMap<K, AK, V, AV> >
begin(CMap<K, AK, V, AV>& cmap) 
  return cmap_iterator_base<CMap<K, AK, V, AV> >(cmap);


template <class K, class AK, class V, class AV>
cmap_iterator_base<CMap<K, AK, V, AV> >
end(CMap<K, AK, V, AV>&) 
  return cmap_iterator_base<CMap<K, AK, V, AV> >();


template <class K, class AK, class V, class AV>
cmap_iterator_base<const CMap<K, AK, V, AV> >
cbegin(const CMap<K, AK, V, AV>& cmap) 
  return cmap_iterator_base<const CMap<K, AK, V, AV> >(cmap);


template <class K, class AK, class V, class AV>
cmap_iterator_base<const CMap<K, AK, V, AV> >
cend(const CMap<K, AK, V, AV>&) 
  return cmap_iterator_base<const CMap<K, AK, V, AV> >();

代码是纯 C++98,因为我不知道您使用的任何版本的 Visual C++ 实现了哪些 C++11 功能。

【讨论】:

以上是关于试图为 MFC 的 CMap 编写一个 for_each 算法的主要内容,如果未能解决你的问题,请参考以下文章

将 MFC CMap 用于 CString int 对

MFC中的CMap类使用

如何使用 cmap 知道目标值使用哪种颜色

如何为 MFC 中的多个按钮编写一个消息处理程序?

:处理MFC

CMap的使用(转)