0

I am trying to call a particular member function in all elements of a tuple using std::apply. This works when I use a lambda but not when defining a free function.

Following Template tuple - calling a function on each element the following code works when using a lambda:

#include<tuple>
#include<iostream>

struct Double  {
     void print() { std::cout << "Double!" << std::endl;}
};

struct Vector  {
    void print() { std::cout << "Vector!" << std::endl;}
};

template<class ...T>
void printAll2(T &...x){(x.print(), ...);}

int main() {

    auto workspace = std::make_tuple<Vector, Double, Vector>({},{},{});
    auto printAll = [](auto &...x){(x.print(), ...);};

    std::apply(printAll , workspace);

    // This does not work.
    //std::apply(printAll2, workspace);

    return 0;
}

However, when I try to use the free function printAll2 I get an error:

<source>:22:5: error: no matching function for call to 'apply'
   22 |     std::apply(printAll2, workspace);
      |     ^~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/    tuple:2955:5: note: candidate template ignored: couldn't infer template argument '_Fn'
 2955 |     apply(_Fn&& __f, _Tuple&& __t)
      |     ^
1 error generated.
Compiler returned: 1

What is the difference between the lambda and the function?

Many thanks!

11
  • 3
    printAll2 has no type (only printAll2<T> does), so you can't pass it around without either specifying a template argument or casting a function pointer (that deduces that the template argument). While a lambda does have a type, since by itself it's not templated, only its operator() is templated. Commented May 15, 2024 at 8:35
  • 1
    There are many duplicates for this, try to find one.. Commented May 15, 2024 at 8:50
  • Other way to fix this issue godbolt.org/z/sdYxb9b43 Commented May 15, 2024 at 10:18
  • @user12002570 are you sure the dupe is appropriate ? From my understanding the current question is different in 2 aspects: (1) The OP mistakenly thought that using std::apply with a given argument will somehow "concretisize" the function template, (2) The OP asked about the difference from a generic lambda, not being aware that in the latter case only the operator() is templated. I attempted to address these issues in my answer. Commented May 15, 2024 at 13:41
  • 1
    "I'm trying to call std::apply with a type as the first argument" You don't, printAll2 isn't a type. The point is that the argument must have a (single) known type to be passed to a function, and you can't determine the type of printAll2. Commented May 16, 2024 at 11:09

1 Answer 1

1

The description of std::apply (from the documentation):

Invoke the Callable object f with the elements of t as arguments.

(emphasys is mine)

The difference between printAll and printAll2:

printAll2 is not a function, but a function template.

A specific function is instantiated by the compiler based on it when you use it with specific arguments. But by itself is is not concrete and is not something that you can apply (it is not a callable object).

In order to apply it you have to specify the template arguments, thus making it a concrete function (which is a callable object):

//-------------------vvvvvvvvvvvvvvvvvvvvvv--------------
std::apply(printAll2<Vector, Double, Vector>, workspace);

printAll on the other hand is a generic lambda.

It is therefore an instance of an unnamed concrete class in which only operator() is templated.
The generic nature of the lambda is taking effect when the lambda is invoked. At that point the compiler instantiates a specific operator() with the relevant parameters.
Internally this class is implemented along the lines of this example:

class __some_internal_class_name__ {
public:
    template<class ...T>
    void operator()(T &...x) const {(x.print(), ...);}
};

Since printAll is a object of that concrete class it can be passed to std::apply.

Summary:
std::apply requires a callable object. Such an object must have a specific concrete type (which printAll is, but printAll2 is not).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.