As per cppref,
Creates new jthread object and associates it with a thread of execution. The new thread of execution starts executing
std::invoke(std::move(f_copy), get_stop_token(), std::move(args_copy)...), ...
And the return type of std::jthread::get_stop_token is std::stop_token.
So, if your f is only used to construct a std::jthread, I believe it's totally ok to be with a const std::stop_token& as its first parameter. There's no lifetime issue.
But if you want to use your f in other places, std::thread for example, there may be a problem.
void f(const std::stop_token&) {}
{
std::stop_source ssrc;
std::stop_token stk{ssrc.get_token()};
std::thread t{f, std::ref(stk)};
}
When stk gets destroyed, the reference is dangled.
In most cases, you should pass by value.
Because copying std::stop_token and std::stop_source is relative cheap.
Also, since std::token is usually stored in a lambda and/or used in another thread, passing by value can avoid lifetime issues.
There're also some cases where you could pass by reference.
Since it's not that cheap compared to raw pointers. It'll be more efficient to pass by reference if you can guarantee the lifetime.
For example, std::stop_callback's constructor, because it'll copy std::stop_token inside, pass by reference will be better.
template<class C>
explicit stop_callback( const std::stop_token& st, C&& cb ) noexcept(/*see below*/);