Is it a templated function that supports different levels of indirection in C ++ 11?

Basically, I'm curious if you can use C ++ 11 templates to make the templated function detect the level of the indirection iterator and compile the function differently depending on that. For example, here's some code that won't compile:

#include <vector>
#include <list>
#include <type_traits>
#include <iostream>

struct MyStruct
{
  int value;
  MyStruct(int value = 42) : value(value) { }
  const int& getInt() const { return value; }
};

typedef std::list<MyStruct> StructList;
typedef std::vector<const MyStruct*> StructPtrVector;

template <typename ITER_TYPE>
const int& getIteratorInt(ITER_TYPE iter)
{
  if (std::is_pointer<decltype(*iter)>::value)
    return (*iter)->getInt(); // Won't compile -> MyStruct has no operator-> defined
  return iter->getInt(); // Won't compile -> MyStruct* has the wrong level of indirection
}

template <typename LIST_TYPE>
void PrintInts(const LIST_TYPE& intList)
{
  for (auto iter = intList.begin(); iter != intList.end(); ++iter)
    std::cout << getIteratorInt(iter) << std::endl;
}

int main(void)
{
  StructList structList;
  StructPtrVector structPtrs;
  int values[5] = { 1, 4, 6, 4, 1 };
  for (unsigned i = 0; i < 5; ++i)
  {
    structList.push_back(values[i]);
    structPtrs.push_back(&structList.back());
  }
  PrintInts(structList);
  PrintInts(structPtrs);

  return 0;
}

      

The obvious situation is when you have a list of objects and then another kind of pointers to objects. And what you want to do is the same for both lists, treating them as lists of objects.

The above code will not compile because it is performing a boolean check that should be done at compile time. I don't know if there is a way to do this using preprocessor macros. I've tried a simple one #if std::is_pointer<decltype(*iter)>::value == true

, but the compiler seems to always think it is false. (I've never tried preprocessor macros before, but that's clearly not the case.)

Any idea if this is possible?

+3


source to share


1 answer


If you want to choose between the two implementations depending on the metapox, for example is_pointer

, use std::enable_if

which works like SFINAE.

template <typename ITER_TYPE>
auto getIteratorInt(ITER_TYPE iter) ->
typename std::enable_if< std::is_pointer< 
        typename std::iterator_traits< ITER_TYPE >::value_type >::value,
    const int& >::type
{
  return (*iter)->getInt();
}

template <typename ITER_TYPE>
auto getIteratorInt(ITER_TYPE iter) ->
typename std::enable_if< ! std::is_pointer< 
        typename std::iterator_traits< ITER_TYPE >::value_type >::value,
    const int& >::type
{
  return iter->getInt();
}

      



This way, when you create a template, only lines of code that are valid for the given template arguments.

I just applied this fix to your code mechanically ... I am not an advocate of using multiple indirection or assuming a fixed implementation is reliable.

+7


source







All Articles