Trying to write a for_each algorithm for MFC's

2019-09-02 04:40发布

Yeah, I know that I should make an iterator, but I need this done fast and writing a proper iterator for anything related to VC++ is beyond frustrating. (This also goes for many other standard things and is buggering up my work load. :( )

So I wrote a for_each() algorithm to deal with the nastiness:

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);
    }
}

But apparently VC++ can't deduce AK and AV for the function template. Is this normal or is this yet another limitation of VC++?

Edit Here is the errors as requested:

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'

This seems to also happen on gcc 4.9.0 with -std=c++11 parameter.

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

int main()
{
  int i;
  fn(i, [](int i){});
  return 0;
}

[DEMO]

And the g++ errors:

/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

2条回答
Rolldiameter
2楼-- · 2019-09-02 05:40

You should make an iterator:

#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> >();
}

Code is pure C++98 since I don't know which C++11 features are implemented by whichever version of Visual C++ you are using.

查看更多
Melony?
3楼-- · 2019-09-02 05:41

C++ lambdas don't have the same type as an std::function with the same signature. This is why the template argument deduction fails - it works with exact types, more or less. Conversions are not considered using template argument deduction. You don't want to use an std::function in your template argument. To force signature, you can check that Func is convertible to an std::function<void(AK, AV)>.

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);
    }
}
查看更多
登录 后发表回答