Search code examples
c++lambdac++20reduceanonymous-function

"Candidate expects 1 argument, 2 provided" error with transform_reduce. Where are extra args?


I am trying to familiarize myself with lambda functions in C++. In my main, I am trying to turn a vector of ints into a string with the transform_reduce function from the C++ numeric library. After a couple hours' research, I can't find the source of the bug in my code. Here is my main cpp file:

#include<vector>
#include<algorithm>
#include<numeric>
#include<iostream>
    
int main(int argc, const char* argv[]) 
{
    std::vector<int> srcVec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    std::vector<int> destVec;
    destVec.resize(srcVec.size());

    auto val = std::transform_reduce(srcVec.begin(), srcVec.end(), destVec.begin(),
                                     std::string(), std::plus{},
                                     [](int a){ return std::to_string(a).append(", "); });

    std::cout << val.substr(0, val.size() - 2) << std::endl; //print string, remove last comma
                    
    return 0;
}

I am passing only one argument to the anonymous function defined as the last argument of the transform_reduce() call. I considered that the plus function is a binary operator type function as well, perhaps contributing to this "too many arguments" error in some way. I've seen many similar examples to my use case online, however, so I don't believe that's the issue.

I've also read about the "capture" syntax of C++ lambdas, wondering whether modifying the anonymous transform function's capture behavior could resolve my issue. However, what I understand the capture syntax to do is change whether the returned value of the anonymous transform function is passed as a shallow copy or as a reference, so I'm not seeing a way that would cause this issue.

I expect the transform_reduce function to transform a copy of each element in srcVec and add it to destVec using the last argument lambda function, then I expect the plus function to be invoked on each element in destVec in conjunction with the initial value generated by string constructor, reducing the destVec to a string through concatenation via the plus function. The string would look like this:

0, 1, 2, 3, 4, 5, 7, 8, 9

I think there could be some parallelism stuff going on, but that is way over my head.

Here is a pastebin with all the error output: https://pastebin.com/UqYngUt7

Thanks for reading this, any help is appreciated :)


Solution

  • There are two versions of std::transform_reduce of interest here. Take a look at cppreference, specifically versions 2 and 3.

    Version 2 takes two ranges Input1 (start and end) and Input2 (only start), as well as an initial accumulator value. Then it also take a binary reduce operation, and a binary transform operation. Reading the explanation below tells us why:

    Applies transform to each pair of elements from the ranges [first; last) and the range starting at first2

    So the transform gets called as: t(r1[0], r2[0]); t(r1[1], r2[1]); etc. (conceptually at least, this is not how iterators actually work).

    This does not look like what you want.

    Version 3, on the other hand has only one input range, and the transform operation is marked as unary. This looks more promising, and in fact:

    int main() 
    {
        std::vector<int> srcVec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    
        auto val = std::transform_reduce(srcVec.begin(), srcVec.end(),
                                         std::string(), std::plus{},
                                         [](int a){ return std::to_string(a).append(", "); });
    
        std::cout << val.substr(0, val.size() - 2) << std::endl; //print string, remove last comma
    }
    

    Compiles and produces: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9