简体   繁体   中英

ensure that an iterator dereferences to a certain type

I have to implement a function that takes an iterator. The iterator must dereference to a certain type, say int:

template<typename iter>
  void f(iter i) {
  // do something here ...
  int t = *i;
  // do something here ...
}

The problem with this code is that if a user calls the function like this

vector<string> v;
v.push_back("aaa");
f(v.begin());

he will see an error pointing to some place in my code, not in his code (which will be confusing to him). I want the error to be in user's code to ease debugging.

GMan already pointed to a method to solve this via compile time assertions. There is another way to do this, which I prefer (it's my favorite C++ technique). You can put constraints on function arguments in a way that the function is ignored for overload resolution if the constraints don't fit. This is quite terrific, because you can fine tune your function overloads to arbitrary conditions. Here's how:

#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <vector>


template<typename Iter> typename
boost::enable_if<
    boost::is_same<typename Iter::value_type,int>,
void>::type
foo(Iter it) { }

int main() {    
    std::vector<int> v; // this is OK
    foo(v.begin());
    std::vector<double> v2; // this is an error
    foo(v2.begin()); }

If you compile this, you will get

b.cc: In function 'int main()':
b.cc:19:16: error: no matching function for call to 'foo(std::vector<double>::iterator)'

This is because the compiler would consider foo() only, if it's argument has a value_type type inside, which is 'int' (This is what the enable_if part means). The second call of foo() can't satisfy this constraint.

enable_if is mentioned a couple of times in SO, just search for it: https://stackoverflow.com/search?q=enable_if

You could do something like this:

#include <boost/type_traits/is_convertible.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/mpl/assert.hpp>

template <typename Iter>
void foo(Iter pIter)
{
    BOOST_MPL_ASSERT_MSG(
        (boost::is_convertible<BOOST_TYPEOF(*pIter), int>::value),
        DEREFERENCED_ITERATOR_MUST_BE_CONVERTIBLE_TO_INT,
        (int));

    // ...
}

#include <vector>
#include <string>

int main(void)
{
    std::vector<std::string> v(5);

    foo(v.begin());
}

Which makes the message quite visible:

error C2664: 'boost::mpl::assertion_failed' : cannot convert parameter 1 from 'boost::mpl::failed ************(__thiscall foo::DEREFERENCED_ITERATOR_MUST_BE_CONVERTIBLE_TO_INT::* ***********)(int)' to 'boost::mpl::assert::type'

But like James says, most compilers give plenty of information to find out what happened anyway.

Given the code in question, most compilers will refer to the point of instantiation in the diagnostic message. For the following, line 16 is the line f(v.begin()); .

Microsoft Visual C++ reports:

> c:\example\main.cpp(16) : see reference to function template instantiation 'void f<std::_Vector_iterator<_Myvec>>(iter)' being compiled
1>          with
1>          [
1>              _Myvec=std::_Vector_val<std::string,std::allocator<std::string>>,
1>              iter=std::_Vector_iterator<std::_Vector_val<std::string,std::allocator<std::string>>>
1>          ]

g++ reports:

main.cpp:16:   instantiated from here

Intel C++ Compiler and Comeau both report:

detected during instantiation of
                "void f(iter) [with iter=std::string *]" at line 16

您需要在泛型类型上设置约束

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM