0

I'm trying to use an algorithm in a function.

Should be very simple.

However, regardless of which algorithm I attempt to use, all of them cause the same error when used in a function.

E0304 no instance of overloaded function "std::begin" matches the argument list

E0304 no instance of overloaded function "std::end" matches the argument list

I am guessing there is some small change that needs to be made.

#include <iostream>
#include <algorithm>

#include "bool_element_option_03.h"
#include "storage.h"

int main()
{
    int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 };

    int arr_copy_value[ELEMENTS];
    
    // array population
    for (int var_create_array_a = 0; var_create_array_a < ELEMENTS; var_create_array_a++)
    {
        arr_copy_value[var_create_array_a] = 0;
    }

    //std::copy(std::begin(arr_value), std::end(arr_value), std::begin(arr_copy_value));
    //std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));

    for (int output = 0; output < ELEMENTS; output++)
    {
        std::cout << "copied decimals: " << arr_copy_value[output] << std::endl;
    }

    bool_element_option_03(arr_value, arr_copy_value);

    return 0;
}
#ifndef _STORAGE_H
#define _STORAGE_H
#define WIN32_LEAN_AND_MEAN

// -----------------------------------------------------------------------------------------------------------------------------------------------------
//                  Constants
// -----------------------------------------------------------------------------------------------------------------------------------------------------

//-----------------------------------------------
const int ELEMENTS = 8;
//-----------------------------------------------

#endif
#include <iostream>
#include <algorithm>

#include "storage.h"

void bool_element_option_03(int arr_value[], int* arr_copy_value)
{
    std::copy(std::begin(arr_value + ELEMENTS), std::end(arr_value + ELEMENTS), std::begin(arr_copy_value + ELEMENTS));
    std::sort(std::rbegin(arr_copy_value + ELEMENTS), std::rend(arr_copy_value + ELEMENTS));

    for (int output = 0; output < ELEMENTS; output++)
    {
        std::cout << "copied decimals: " << arr_copy_value[output] << std::endl;
    }
}

If I take these algorithms out of the function and put them in main(), they work as they should.

Should I intentionally overload this function (so I can use algorithms in it)?

Overloading this function is not my intention. I'm not calling it multiple times with different arguments.
This function is only being called once.

O'Neil
  • 3,790
  • 4
  • 16
  • 30
  • 2
    and do not officially supply `std::begin` and `std::end`, so all bets are off. – user4581301 Nov 23 '22 at 23:14
  • 2
    As a function parameter `long double arr_decimals[]` is `long double* arr_decimals` - and `std::begin` and `std::end` do not work with pointers (since they have no way to actually find out where a pointed to array ends) – UnholySheep Nov 23 '22 at 23:22
  • so what changes need to be made given the pointers? – Retro Epiphany Nov 23 '22 at 23:25
  • 1
    If you can, switch to actual containers (such as `std::vector`). If you need to stick with pointers you will also need to supply the sizes to the function, so that you can replace `std::end(arr_decimals)` with something like `arr_decimals + arr_size` – UnholySheep Nov 23 '22 at 23:29
  • If we could see a [mre], we could recommend an appropriate solution. – Paul Sanders Nov 23 '22 at 23:36
  • replacing (std::begin(arr_decimals) with (std::begin(arr_decimals + PROCED_ELEMENTS) did not remove or change the error – Retro Epiphany Nov 23 '22 at 23:42
  • I modified it for a minimal reproducible example. – Retro Epiphany Nov 24 '22 at 00:33
  • Whether it is void func(int a[]) or void func(int * a), when the array is passed as a function parameter, it will degenerate into a pointer, so there will be problems when using the array name as the parameter of begin(). – Yujian Yao - MSFT Nov 24 '22 at 07:47
  • @YujianYao-MSFT yes, while technically true, the the errors still exists. – Retro Epiphany Nov 24 '22 at 19:04
  • Thank you for the [mre]. It helps. But what is wrong with using `std::array` or `std::vector` (passing these by reference when you don't want to make a copy at the call site)? Using C-style arrays is the reason you're having so many problems with this code. – Paul Sanders Nov 25 '22 at 13:54
  • nothing, std:array and std::vector are preferred, but right now I am just trying to quickly get this done. For me c-style is faster. – Retro Epiphany Nov 27 '22 at 01:53
  • That's not a good reason, look how much time you've wasted already. – Paul Sanders Nov 27 '22 at 11:38
  • wow. shouldn't you be in youtube or one of the fringe subreddits? seriously, given a) the demographs of internet forums being mostly kids and high schoolers who don't have enough experience to give advice in programming, to begin with and b) the fact that you haven't seen a single line of code from the actual project (just an example of how I might be using it), I am going to conclude, Troll, that you don't know what you are talking about and are in fact disposed to exaggerate the value of your opinion. good day, sir. – Retro Epiphany Nov 28 '22 at 20:55

2 Answers2

0

The error text is explicit:

E0304 no instance of overloaded function "std::begin" matches the argument list

So, what are those instances? See e.g. https://en.cppreference.com/w/cpp/iterator/begin

template< class C >
constexpr auto begin( C& c ) -> decltype(c.begin());   

template< class C >
constexpr auto begin( const C& c ) -> decltype(c.begin());

Returns exactly c.begin(), which is typically an iterator to the beginning of the sequence represented by c. If C is a standard Container, this returns C::iterator when c is not const-qualified, and C::const_iterator otherwise.

template< class T, std::size_t N >
constexpr T* begin( T (&array)[N] ) noexcept;

Returns a pointer to the beginning of the array.

The last one seems promising, its argument is a reference to an array (note how it's declared) and that is the overload used here:

int main()
{
    int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 };
    int arr_copy_value[ELEMENTS];
    
    // ...
    
    std::copy(std::begin(arr_value),       // <---
              std::end(arr_value),
              std::begin(arr_copy_value)); // <---     
    // ...
}

That's because both arr_value and arr_copy_value are arrays, there.

When the compiler reads the refactored code, on the other end, it can't find a suitable overload:

void bool_element_option_03(int arr_value[], int* arr_copy_value)
{   //                                   ^^ That's kind of misleading
    std::copy(std::begin(arr_value + ELEMENTS),
              std::end(arr_value + ELEMENTS),
              std::begin(arr_copy_value + ELEMENTS));
    // ...
}

Here, both arr_value and arr_copy_values are pointers, not arrays and there's no overload of std::begin() (and the likes) accepting a pointer in the Standard Library.

Given the call bool_element_option_03(arr_value, arr_copy_value); in main and due to array to pointer decay(1), they point to the respective first elements of the two arrays declared in main. They are local variables which happen to have the same name of the variables in main.

Besides, arr_value + ELEMENTS points one after the last element of the array. It's a valid pointer as long as it's not dereferenced.

You can make this function works, without changing the call site, by directly passing those pointers(2) to the SL algorithm functions:

void bool_element_option_03(int const *arr_value, int *arr_copy_value)
{
    std::copy(arr_value, arr_value + ELEMENTS, arr_copy_value);
    std::sort(arr_copy_value, arr_copy_value + ELEMENTS, std::greater{});
    // ...
}

This can be generalized by passing the size too:

void bool_element_option_03( size_t n, int const *src, int *dest)
{ //                         ^^^^^^^^ 
  std::copy(src, src + n, dest);
  std::sort(dest, dest + n, std::greater{});
  // ...
}

To generalize a bit more, you can modify the function signature into a template accepting references of array:

template< class Type, std::size_t N>
void bool_element_option_03(Type const (&arr_value)[N], Type (&arr_copy_value)[N])
{
    std::copy(std::begin(arr_value), std::end(arr_value), 
              std::begin(arr_copy_value));
    std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
    // ...
}

Which can still be called in main with bool_element_option_03(arr_value, arr_copy_value);, except that, now, there's no decay and the arrays are actually passed by reference and the correct overload of std::begin can be selected.


To mimic the Standard Library algorithms, we can instead pass iterators directly:

template< class SourceRandomIterator, class DestRandomIterator >
void copy_and_sort( SourceRandomIterator source_first
                  , SourceRandomIterator source_last
                  , DestRandomIterator dest_first )
{
    auto dest_last{ std::copy(source_first, source_last, dest_first) };
    std::sort(dest_first, dest_last, std::greater{});
    // ...
}

But we also have to modify the call in main:

bool_element_option_03(std::cbegin(arr_value), std::cend(arr_value), 
                       std::begin(arr_copy_value));

Since C++20, we can use std::span as arguments.

void bool_element_option_03(std::span<const int> arr_value,
                            std::span<int> arr_copy_value)
{
    std::copy(std::begin(arr_value), std::end(arr_value), 
              std::begin(arr_copy_value));
    std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
    // ...
}

Or take advantage of the ranges library:

template< std::ranges::random_access_range SourceRange
        , std::ranges::random_access_range DestRange >
void bool_element_option_03(SourceRange const& source,
                            DestRange& dest)
{
    std::ranges::copy(source, dest);
    std::ranges::sort(dest | std::ranges::views::reverse );
    // ...
}

In both cases, this function can be called from main as bool_element_option_03(arr_value, arr_copy_value);


1) What is array to pointer decay?
2) See e.g. How are iterators and pointers related? and how std::sort is declared.

Bob__
  • 12,361
  • 3
  • 28
  • 42
  • thanks, I know what an array to pointer decay is. and I know what an iterator is. I took three classes in C++ in college, but that was 20+ years ago. Recently I've read two C++ books. 'How to Program' by Paul and Harvey Deitel and 'C++ Through Game Programming' by Micheal Dawson but neither of them show examples of using an algorithm inside of a function. Both books focused on using algorithms inside of main(). Currently I am reading 'The C++ Programming Language' by Stroustrup. It has examples of algorithms in functions but the examples aren't minimal reproducible :P – Retro Epiphany Nov 24 '22 at 19:03
  • I'm not a n00b to programming concepts, but I am having trouble wrapping my head around the proper syntax needed using iterastors to containers while in a function. I spent the last seven years using GML. That was a mistake. – Retro Epiphany Nov 24 '22 at 19:19
  • I am working on a 'small' ~800 line procedural engine. It works. All of it; as long as everything stays inside Main() lol. I need to encaspulate it into a group of ultility functions within an easily reusable class. Woud you mind digging a but deeper? – Retro Epiphany Nov 24 '22 at 19:31
  • when I use your method for my project a ton more errors are created... mainly arounds std:: and for loops in and out of that function. – Retro Epiphany Nov 24 '22 at 19:32
  • while your solution worked for that minimalized example, the probability that every (production worthy) programmer, in the world, turns every function they have with a containter and an algorithm in it into a template seems very small. Would you be willing to explain why you choose to use this process instead of a different approach? Would you be willing to put a little more time into this? – Retro Epiphany Nov 24 '22 at 19:34
  • how is this an overload: int main() { int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 }; int arr_copy_value[ELEMENTS]; // ... std::copy(std::begin(arr_value), std::end(arr_value), std::begin(arr_copy_value)); // ... } – Retro Epiphany Nov 24 '22 at 19:38
  • @RetroEpiphany *"the probability that every (production worthy) programmer, in the world, turns every function they have with a containter and an algorithm in it into a template seems very small."* Well, I don't have any statistic, but the percentage of open source libraries written using templates is probably way bigger than you think. Try checking some of those. *"Would you be willing to explain why you choose to use this process instead of a different approach?"* There are many more ways of doing this that I didn't even mentioned. See e.g. https://godbolt.org/z/rd73GaM61 – Bob__ Nov 24 '22 at 20:32
  • @RetroEpiphany *"when I use your method for my project a ton more errors are created..."* Without any detail, not even how did you compile it, I can't help you. *"how is this an overload:"* I'm not sure to understand what you mean, there. – Bob__ Nov 24 '22 at 20:35
  • @RetroEpiphany *"I know what an array to pointer decay is. and I know what an iterator is. I took three classes in C++ in college, but that was 20+ years ago."* That should be enough to come up with something like https://godbolt.org/z/f9cd6efeE ;). – Bob__ Nov 24 '22 at 20:46
  • wow, your confusing simple number gen with procedural engines and large number theory, or perhaps education and intelligence but w/e. This is the internet. jab accepted. – Retro Epiphany Nov 24 '22 at 21:17
0

thanks for your help everyone! this is what I decide to go with.

#include <iostream>
#include <algorithm>

constexpr size_t ELEMENTS{ 8 };

void show(const int(&arr)[ELEMENTS])
{
    for (const auto& c : arr)
        std::cout << c;

    std::cout << '\n';
}

void bool_element_option_03(const int(&arr_value)[ELEMENTS], int(&arr_copy_value)[ELEMENTS])
{
    std::copy(std::begin(arr_value), std::end(arr_value), std::begin(arr_copy_value));
    std::sort(std::rbegin(arr_copy_value), std::rend(arr_copy_value));
    
    std::cout << "\nReverse sorted:\n";
    show(arr_copy_value);
}

int main()
{
    const int arr_value[ELEMENTS]{ 1, 2, 9, 4, 5, 6, 7, 8 };
    int arr_copy_value[ELEMENTS]{};

    std::cout << "\nOriginal order:\n";
    show(arr_value);
    bool_element_option_03(arr_value, arr_copy_value);
}