Force selecting non-variadic template oveload - c++

I have two overloads:
template<typename... T>
void f(T&&... args);
template<typename ContainerType>
void f(const ContainerType& container);
how can I make it choose the second overload if called as e.g.
f(std::vector());
(or any other type that has e.g. begin() method)
https://godbolt.org/z/osdEMe7rK

An rvalue reference binding to an rvalue is "better" than a const lvalue reference binding to an lvalue. So you need to make both bindings equally good by changing the second overload to also take a forwarding reference:
template <typename ContainerType>
void f(ContainerType&& container);
Now, if both overloads are viable, the non-variadic one will be selected because it is "more specialized". (The "more specialized" tiebreaker is only applied if both overloads are equally good for every argument in the call.)
If you also want to constrain it so that container.begin() must be well-formed, you can do so in C++17 as follows:
template <typename ContainerType>
auto f(ContainerType&& container) -> std::void_t<decltype(container.begin())>;
Now, this overload will be selected if there is a single argument and the constraint is satisfied. If either condition doesn't hold, the variadic overload will be selected.
(To be really pedantic, you might want to use static_cast<ContainerType&&>(container).begin() to do perfect forwarding inside the decltype specifier.)

Use a lvalue reference as follows:
#include <vector>
#include <functional>
template<typename... T>
void f(T&&... args);
template<typename ContainerType>
void f(const ContainerType& container)
{}
int main()
{
std::vector<int> vec;
f<decltype(vec)>(vec);
}
The void f(T&&... args); declare args that are passed by rvalue reference.
Edit 1
If you want a RValue reference you can change void f(const ContainerType& container) to:
template<typename... T>
void f(T&&... args);
template<typename ContainerType>
void f(ContainerType&& container)
{
std::cout << "call specialized f()";
}
int main()
{
f(std::vector<int>());
}

Related

forwarding in c++ functions with auto type parameters

With forwarding functions using a template parameter, the idiom is to forward using the typename T, e.g.
template <typename T>
void fwd(T &&x) {
some_func(std::forward<T>(x));
}
Equivalent approach when using a forwarding function (or lambda) that takes an auto type parameter:
void fwd(auto &&x) {
some_func(std::forward<decltype(x)>(x));
}
In the above decltype(x) is not necessarily the same as the type of T in the previous example. E.g. fwd(3) deduces T to int while decltype(x) is int&&.
Can someone explain why we use T in one case vs. decltype(x) as the template parameter to std::forward above? Is the end result always guaranteed to be identical based on the implementation of std::forward?
void fwd(auto &&x) is a c++20 shorthand for template <typename T> void fwd(T &&x). But in this case you don't have type to refer to. You can write this:
void fwd(auto &&x) {
using T = std::remove_reference_t<decltype(x)>;
some_func(std::forward<T>(x));
}
Here T equals to T in original version, but it doesn't matter much.
std::forward<T> and std::forward<std::remove_reference_t<T>> are equal, because its signature defined like this:
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;

Universal reference deduction using the same_as concept

I'm trying to implement a push function for a blocking queue which accepts a universal reference as it's template parameter, but requires that the template argument be the same type as the queue's element type:
template <typename ValueType>
class shared_queue
{
public:
template <typename Arg>
requires std::same_as<Arg, ValueType>
void push(Arg&& arg);
private:
std::deque<ValueType> container_;
};
However, I'm not quite sure how is universal reference deduction supposed to work in this case, or if it works at all for that matter. The following code:
shared_queue<int> sq;
int x{ 5 };
sq.push(x); // won't compile
sq.push(5); // all good
does not compile. The compiler complains that:
I'm pretty sure I'm misunderstanding something but I don't know what.
You need to remove_reference from Arg for same_as to consider the int& to x and int the same type. You may also want to remove const in case you have const int x and pass that as a parameter. Removing both (+ volatile) can be done with std::remove_cvref_t:
template <typename Arg>
requires std::same_as<std::remove_cvref_t<Arg>, ValueType>
void push(Arg&& arg) {
container_.push_back(std::forward<Arg>(arg));
}
Another option would be to allow for any arguments that can be used to construct a ValueType:
template <class... Args>
requires std::constructible_from<ValueType, Args...>
void emplace(Args&&... args) {
container_.emplace(container_.end(), std::forward<Args>(args)...);
}

C++: static assert that functor's argument is const reference

I'm writing a template function in C++17 which accepts a functor F as argument and I want to restrict the passed in functor to have only one constant reference argument, where T can be any type.
for example:
template <class T> struct my_struct{
std::vector<T> collection;
template <class F> std::vector<T> func(F f){
static_assert(
// CONDITION HERE!,
"f's argument is not const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
Obviously, in case f is [](auto v){return true;} the resulting vector returned from func will have empty elements (because those are moved before adding to resulting container). So, I need to restrict possible input functors to [](const auto& v){}.
I have tried something like this:
static_assert(
std::is_invocable_v<F, const T&>,
"f's argument is not const reference"
);
But then func([](auto v){}) does not trigger the assertion, because T is copyable in my case.
The func([](auto& v){}) also passes the test, because auto can be const T.
But I need to limit possible lambdas to be func([](const auto& v){}).
You might write traits (with its limitations), something like:
template <typename Sig> struct callable_traits;
template <typename Ret, typename ...Args>
struct callable_traits<Ret(*)(Args...)>
{
using args = std::tuple<Args...>;
};
// add specialization for C-ellipsis too
template <typename Ret, class C, typename ...Args>
struct callable_traits<Ret(C::*)(Args...) const>
{
using args = std::tuple<Args...>;
};
// add specialization for variant with C-ellipsis, cv-qualifier, ref-qualifier
template <class C>
struct callable_traits<C> : callable_traits<&C::operator()>{};
Limitation of the traits: doesn't handle templated operator() (as for generic lambda), overloaded operator().
And then
template <class T> struct my_struct{
template <class F> void func(F f){
static_assert(
std::is_same_v<std::tuple<const T&>, typename callable_traits<F>::args>,
"f's argument is not const reference"
);
// here goes some code which can possibly call f with rvalue
// reference argument, so I want to avoid situation when the
// argument object is moved or modified. I don't have control
// over this code because it an STL algorithm.
}
};
I could be misunderstanding what you're trying to do but, as I read it, you want to accept a callable, then pass some argument to it with a guarantee that the argument cannot be changed (you don't want someone to accept the argument as a non-const l-value reference or an r-value reference. If so, then std::is_invocable should be enough:
#include <type_traits> // for std::is_invocable
#include <functional> // for std::invoke
template <class parameter_t> struct my_struct {
template <typename callable_t>
void function(callable_t const &callable) requires (
std::is_invocable<callable_t, parameter_t const &>::value
) {
// . . .
std::invoke(callable, parameter_t{});
// . . .
}
};
Then:
int main() {
my_struct<int>{}.function([](int const&){}); // Fine
my_struct<int>{}.function([](int){}); // Fine, a copy of the parameter is made when the lambda is invoked.
my_struct<int>{}.function([](int &){}); // error: no matching member function for call to 'function'
my_struct<int>{}.function([](int &&){}); // error: no matching member function for call to 'function'
}
(You can play around with it here)
A possible problem is that this method does allow a copy to be made, but if the main goal is to protect the variable you hold from changes this should be good enough.
P. S. I know I used c++20 requires clause as a way of future-proofing the answer, but it should be trivial to convert it to if constexpr, static_assert or any other way you prefer.
I finally managed to achieve it in the following way:
template <class T> struct my_struct{
std::vector<T> collection;
struct noncopyable_value_type : T{
noncopyable_value_type(const noncopyable_value_type&) = delete;
noncopyable_value_type& operator=(const noncopyable_value_type&) = delete;
};
template <class F> std::vector<T> func(F f){
static_assert(
std::is_invocable_v<F, noncopyable_value_type>,
"f's argument must be const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
But still, the problem here is that it only works with generic lambdas.

std::span<const T> as parameter in function template

In the following library functions f and g, I use std::span<const T> to remind the user of the library of the contract that f and g will not modify the contents of the span. The user of the library holds std::span<int> a, which is convertible to std::span<const int>. Hence, the user can call g(a) as expected. However, the user cannot call f(a), since f is a function template and a is not of the required type; conversions do not apply here. Can you see a (sane) way to have f take in std::span<const T> while still accepting std::span<T>?
#include <span>
void g(const std::span<const int>& a) {}
template <typename T>
void f(const std::span<const T>& a) {}
int main()
{
std::span<int> a;
// OK
g(a);
// No match
f(a);
// OK
f((std::span<const int>)a);
}
It is possible to add an overload like this:
template <typename T>
void f(const std::span<T>& a) {
return f((std::span<const T>)a);
}
But I'm not sure if I count this as sane, since then I would be writing these overloads to every function template which takes in std::span<const T>.
EDIT: Not the same case, but if f also happens to take in another parameter which mentions T, then one can do the following:
template <typename T> using NoDeduction = std::type_identity_t<T>;
template <typename T>
void f(NoDeduction<std::span<const T>> a, const T& b) {}
After which f(a, 1); works. This is a partial solution. The original problem still remains.
EDIT 2: I was wondering whether the above technique works also when we include span's compile-time size:
template <typename T, std::size_t N>
void f(const std::span<NoDeduction<const T>, N>& a, const T& b) {}
Gcc 10.1 compiles it, MSVC 16.6.2 and Clang 10.0.0 don't. I filed bug reports for both MSVC and Clang.
You can generalize your overload to be a utility like std::as_const:
template<class T>
std::span<const T> const_span(std::span<T> s) {return s;}
The caller then has to decorate their calls with mutable spans, but in so doing indicates to the reader that no modification is allowed.
Another, unconventional workaround would be to make your function be a class and use deduction guides:
template<class T>
struct f {
f(std::span<const T>);
// operator ReturnType();
};
template<class T> f(std::span<T>)->f<T>;
It’s debatable whether the interface describes the intent here.
It is unable to make the type conversion because it fails to deduce the type as f is a template.
If you want to write template function f that accepts various input types - you'd better write it as
template<typename Viewer>
void f(const Viewer& a){...}
If you want to work with span then you can implement it in two steps:
template<typename T>
void f_impl(std::span<const T> a){...}
template<typename Viewer>
void f(const Viewer& a)
{
f_impl(std::span<const typename Viewer::value_type>(a.data(),a.size()));
}
P.S. with span you should normally take copy of it instead of "const reference". This is more efficient.

Is it possible to write two template functions as one when the only difference is the const-ness of an argument?

I have something like:
template <class Foob, class T>
void do_it(Foob& f, T& t) { f.proc(t); }
template <class Foob, class T>
void do_it(Foob& f, const T& t) { f.proc(t); }
Some Foobs take const T&s while other Foobs take just T&s, or the same Foob may even have two procs, one for the const case, one for the non-const case, that do different things.
However, the code of do_it is the same: it's just f.proc(t). Sometimes the code is many lines, but still identical. Is there any way to write do_it as just one function? Something like this, though this would clearly not work:
template <class Foob, class MaybeConst, class T>
void do_it(Foob& f, MaybeConst T& t) { f.proc(t); }
Actually if the t argument is always an lvalue then you only need the first overload! If the lvalue has type const U, then the template parameter T is deduced as const U and the instantiated function's second parameter type will be const U&, which will bind to the const lvalue just fine.
The second overload is only needed if the argument is an rvalue. But instead of making that a special case, why not just use perfect forwarding?
// works for both lvalues and rvalues with any cv-qualification
template <class Foob, class T>
void do_it(Foob& f, T&& t) { f.proc(std::forward<T>(t)); }