Search code examples
c++arraysauto

How to initialize auto-type array of multiple types objects


I need to initialize an array with arguments of a tuple. So I tried to set it like this :

auto average_intensity(Ts&&... args)
{
    auto pixels[] = {args... };
    // [...]
}

But I get the following error :

‘pixels’ declared as array of ‘auto’ auto pixels[] = {args... };

How can I fix this ?

Here is the full code :

#include <iostream>
#include <type_traits>
#include <string>
#include <utility>
#include <tuple>


using namespace std;

struct gray_pixel{
  uint8_t gray;
  auto intensity(){
      return gray - 127;  
  }
};


struct rgb_pixel{
  float red, green, blue;
  auto intensity(){
      return (red - 127 + green -127 + blue - 127) * 255 / 3;  
  }
};


template<typename T>
auto intensity(T pixel){
    return pixel.intensity();
}
//find the average intensity of multiple pixels from different types (gray_pixel or rgb_pixel)
template <typename... Ts>
auto average_intensity(Ts&&... args)
{
    auto pixels[] = {args...};

    auto sum = 0;
    int count = 0;
    for(auto pixel : pixels){
        sum +=intensity(pixel);
        count++;
    }
    return sum/count;
}


int main()
{

    gray_pixel pixel_gray = {255};
    rgb_pixel pixel_rgb = {0,0,250};

    cout << "sizeof(pixel_gray): " << sizeof(pixel_gray) << endl;
    cout << "sizeof(pixel_rgb): " << sizeof(pixel_rgb) << endl;

    cout << "intensity(pixel_gray): " << intensity(pixel_gray) << endl;
    cout << "intensity(pixel_rgb): " << intensity(pixel_rgb) << endl;

    cout << "average_intensity(pixel_gray, pixel_rgb): " << average_intensity(pixel_gray, pixel_rgb) << endl;
    return 0;
}

Solution

  • You need a tuple instead of an array. However then you can't use a for loop but you have to exploit compile-time methods as the fold expression in the code below:

    #include <tuple>
    #include <iostream>
    
    using namespace std;
    
    struct gray_pixel  {
        uint8_t gray;
        auto intensity() const {
            return gray - 127;
        }
    };
    
    
    struct rgb_pixel{
        float red, green, blue;
        auto intensity() const {
            return (red - 127 + green -127 + blue - 127) * 255 / 3;
        }
    };
    
    
    template<typename T>
    auto intensity(const T& pixel){
        return pixel.intensity();
    }
    //find the average intensity of multiple pixels from different types (gray_pixel or rgb_pixel)
    template <typename... Ts>
    auto average_intensity(Ts&&... args)
    {
    
        // you actually don't need this here, but I leave it
        // to clarify how to capture the args in a tuple.    
        // you can use std::apply to perform calculations on the elements
        auto tup = std::forward_as_tuple(std::forward<Ts>(args)...);
    
        const float count = sizeof...(Ts);
    
        // a lambda that gets the intensity of a single pixel
        auto get_intensity = [](auto&& pix) {
            return static_cast<float>(std::forward<decltype(pix)>(pix).intensity());
        };
        // fold expression to sum all the intensities of the args
        auto sum = (get_intensity(args) + ...);
        return sum/count;
    }
    
    
    int main()
    {
    
        gray_pixel pixel_gray = {255};
        rgb_pixel pixel_rgb = {255,127,127};
    
        cout << "sizeof(pixel_gray): " << sizeof(pixel_gray) << endl;
        cout << "sizeof(pixel_rgb): " << sizeof(pixel_rgb) << endl;
    
        cout << "intensity(pixel_gray): " << intensity(pixel_gray) << endl;
        cout << "intensity(pixel_rgb): " << intensity(pixel_rgb) << endl;
    
        cout << "average_intensity(pixel_gray, pixel_rgb): " << average_intensity(pixel_gray, pixel_rgb) << endl;
        return 0;
    }
    

    This was the answer to the original question:

    You could try it like this:

    If all of your Ts are of the same type, you can drop the std::common_type and use the type of the first element instead. Also I'd suggest to use std::array instead of a C-style array.

    #include <utility>
    #include <array>
    
    template<typename... Ts>
    auto average_intensity(Ts&&... ts) {
    
        using Auto = std::common_type_t <std::decay_t<Ts>...>;
    
        std::array<Auto, sizeof...(Ts)> pixels {std::forward<Ts>(ts)...};
    
        // or if you really want an C-style array
        Auto pixels[]{std::forward<Ts>(ts)...};
    
    }