简体   繁体   中英

Compile error with variadic templates and overloaded << operator

I'm trying to create a class that would store a tuple of vectors. I have a strange compilation error whose origin I don't understand.

The code:

#include <iostream>
#include <tuple>
#include <vector>

/// Suppose T is a pack of primitive types
/// VectorTuple defines a tuple of vectors of these types

template <typename... T>
struct VectorTuple;

template <typename T>
struct VectorTuple<T>
{
  using data_type = std::tuple<std::vector<T>>;
};

template <typename T1, typename T2>
struct VectorTuple<T1, T2>
{
  using data_type = std::tuple<std::vector<T1>, std::vector<T2>>;
};

template <typename T1, typename T2, typename T3>
struct VectorTuple<T1, T2, T3>
{
  using data_type = std::tuple<std::vector<T1>, std::vector<T2>, std::vector<T3>>;
};

// ----------------------------------------------------------------------------

template <size_t I, size_t N, typename... T>
struct DataAlgorithm
{
  static void resize(typename VectorTuple<T...>::data_type &data, const size_t size)
  {
    std::get<I>(data).resize(size);
    DataAlgorithm<I + 1, N, T...>::resize(data, size);
  }
};

// ----------------------------------------------------------------------------

template <size_t N, typename... T>
struct DataAlgorithm<N, N, T...>
{
  static void resize(typename VectorTuple<T...>::data_type &data, const size_t size)
  {
    std::get<N>(data).resize(size);
  }
};

// ----------------------------------------------------------------------------

template <typename... StoredTypes>
class MultiArray
{

public:
  enum
  {
    NFields = sizeof...(StoredTypes)
  };

  using value_types = std::tuple<StoredTypes...>;

  void resize(const size_t size)
  {
    DataAlgo<0, NFields - 1, StoredTypes...>::resize(m_values, size);
  }

private:
  template <size_t I, size_t N, typename... T>
  using DataAlgo = DataAlgorithm<I, N, T...>;

  using storage_type = typename VectorTuple<StoredTypes...>::data_type;

  /// The actual data stored
  storage_type m_values;
};


// This doesn't compile when array is const&, but works with non-const reference
template <typename... StoredTypes>
std::ostream &operator<<(std::ostream &os, const MultiArray<StoredTypes...> &array)
{
  os << std::endl; // This is line 86 in the compiler error message
  return os;
}

int main()
{
  MultiArray<double, int, float> array;

  return 0;
}

I'm not able to overload the "<<" operator with const MultiArray<...>& as parameter. Clang 3.9.0 says:

compile_error.cpp: In instantiation of ‘class MultiArray<>’:
compile_error.cpp:86:14:   required from here
compile_error.cpp:75:71: error: invalid use of incomplete type ‘struct VectorTuple<>’
   using storage_type = typename VectorTuple<StoredTypes...>::data_type;
                                                                       ^
compile_error.cpp:9:8: note: declaration of ‘struct VectorTuple<>’
 struct VectorTuple;

If I understand correctly, the compiler is not able to deduce the parameter types and tries to instantiate VectorTuple with no parameters whatsoever. Could you please help me fix this? Thank you.

This problem seems to be similar to the one described in this post .

I also have a bonus question (but this should perhaps go to another post). Is there a more generic way of transforming a parameter pack of primitive types to a tuple of vectors of these types? It would be nice to replace the explicit enumeration of different variants of VectorTuple based on increasing number of template parameters by something like this:

template<typename ... T> struct TransformToVectors { /* ?? implementation ?? */ };

such that

TransformToVectors<double, int, float>::type

would be an alias for

std::tuple<std::vector<double>, std::vector<int>, std::vector<float>>

I tried to build the type recursively from instances with lower number of template parameters, but didn't get very far.

For the bonus question

Is there a more generic way of transforming a parameter pack of primitive types to a tuple of vectors of these types? It would be nice to replace the explicit enumeration of different variants of VectorTuple based on increasing number of template parameters by something like this:

 template<typename ... T> struct TransformToVectors { /* ? implementation ?? */ }; 

such that

 TransformToVectors<double, int, float>::type 

would be an alias for

 std::tuple<std::vector<double>, std::vector<int>, std::vector<float>> 

What's wrong with

template<typename ... T>
struct TransformToVectors
 { using type = std::tuple<std::vector<T>...>; };

?

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