4

Why is it legal to pass generic lambdas to std::thread() without any specialization, while, on the other hand, this is illegal with function templates.

The following demonstrates my query.

#include <thread>

template <class T>
void f(T t)
{
}

int main()
{
    std::thread t([](auto i){}, 1);  // Works
    std::thread t(f, 1);          // Doesn't work
    std::thread t(f<int>, 1);     // Works

    t.join();
    return 0;
}
5
  • 1
    Since deduction type can't be done based on internal implementation. It happens based on function (here constructor) arguments. Now lambda is instance of specific type with function which is a template. Take a look on this: cppinsights.io/s/e0e75c1c Commented Jul 25, 2023 at 10:11
  • @MarekR could you please elaborate... Commented Jul 25, 2023 at 10:14
  • 5
    A template function is just a template, a blueprint for a function, but it's not an actual function. You need to say which explicit function you want to use. A lambda, even a generic one like you have, is not a template. It's an actual object with a specific function-call operator overload. Commented Jul 25, 2023 at 10:16
  • 2
    Again generic lambda is a solid type (see cppinsights.io/s/e0e75c1c). So not a template is passed to thread, but object with well defined type. Then when lambda is invoked there is attempt to instantiate member function template which is operator()(auto). Commented Jul 25, 2023 at 10:20
  • 1
    A lambda is effectively an instance of a compiler-defined type with an operator() on it. So, std::thread t([](auto i){}, 1); is basically like: struct <generated> { void operator()(auto i){} }; <generated> instance; std::thread t(instance, 1); Commented Jul 25, 2023 at 20:58

1 Answer 1

9

C++ consists of many different kinds of things. Some of these things are: objecs, types, and templates. They are completely different things, that serve their own distinct, unique purposes. There's a right place in a C++ program to use an object, a right place to use a type, and a right place to use a template. When you have a right place for an object, you can't use a type or a template. When you have a right place to use a type you can't use an object or a template. When you have a right place to use a template you can't use an object or a type. This is fundamental to C++.

The parameters to a function call, or a constructor, are always objects. They are never types, or templates.

    std::thread t([](auto i){}, 1);  // Works

Both parameters are objects. This is correct. A lambda is an object, an instance of a unique, anonymous type. 1 is also an object: an integer whose value is 1.

    std::thread t(f, 1);          // Doesn't work

f is not an object. It is a template.

    std::thread t(f<int>, 1);     // Works

This f<int> is an object, a pointer to a function that was instantiated from a template.

An identifier that corresponds to a name of a template, by itself, results in a template. It does not result in a type, or an object. When you specify template parameters you end up with a type, and in a special case of a type that refers to a template function you get a pointer to the template function instance, an object (just like using the name of an ordinary non-template function, by itself, gives you a pointer to that function).

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.