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

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.

Related

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)...);
}

How to best solve "void foo( const T& t = T() )" when T==void

I have a function which has an option parameter of type T.
template<typename T>
void foo( const T& t = T() )
{ t.stuff; }
This was all fine but I now have a scenario where T becomes void. In this case I expected a no-operation empty function. The only workable solution I have requires three separate declarations and I have many of these sort of methods:
template<typename T>
void foo( const T& t)
{ t.stuff; }
template<typename T>
inline void foo()
{ foo(T()); }
template<>
inline void foo<void>() {}
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
A simpler solution (in that there are only 2 overloads) would be something like this:
template<typename T>
void foo( const T& t = T() ) {
t.stuff;
}
template<typename T>
std::enable_if_t<std::is_void_v<T>>
foo() {}
// void foo(const T&); is the only viable overload for non-void T,
// since std::enable_if_t SFINAEs
// void foo(); is the only viable overload for void T,
// since const T& forms a reference to void
And this can be slightly shortened with alias templates since you use this pattern a lot:
template<typename T, typename TypeIfVoid = void>
using if_void = std::enable_if_t<std::is_void_v<T>, TypeIfVoid>;
template<typename T>
void foo(const T& t = T()) {
t.stuff;
}
template<typename T>
if_void<T> foo() {}
Two default template parameters will do it:
template<class T> using FallbackT = std::conditional_t<std::is_void_v<T>, int, T>;
template<class T = int&, class U = FallbackT<T>>
void foo(U const& t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Example.
int& is the default for T so that compilation fails (default-constructing a reference at U()) if someone attempts to call foo() without either a template argument or an actual argument (try uncommenting it in the example).
I'm using int within FallbackT alias template since U just needs to be something that is default-constructible - this isn't visible to the user.
If you want to be fancy (and prevent misuse) you could add a variadic guard and use closure types:
template<
class T = decltype([]{})&,
class...,
class U = std::conditional_t<std::is_void_v<T>, decltype([]{}), T>>
void foo(U const& t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Here, the variadic guard prevents specifying U explicitly, as e.g. foo<int, long>(); the closure types make it impossible for someone to call foo with those types by any other means - this is probably unnecessary.
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
Well... without a 3rd declaration, yes (you can use only one).
More elegant... it's question of tastes, I suppose.
More coincise syntax... well... almost the same, I suppose.
Anyway, I propose the following version, if constexpr and std::conditional_t based.
template <typename T,
typename U = std::conditional_t<std::is_same_v<T, void>,
int,
T>>
void foo (U const & u = U{})
{
if constexpr ( std::is_same_v<T, void> )
{ /* do nothing */ }
else
{ /* do u.stuff; */ }
}

Deducing the template parameter of a templated class argument: const issue

I think the problem is fairly common, so there should be a known solution. I came up with one, but I'm not really satisfied, so I'm asking here, hoping someone can help.
Say I have a function, whose signature is
template<typename T>
void foo(const MyArray<const T>& x);
The const in the template parameter is to prevent me from changin the array content, since (for reasons beyond this question), the accessors ([] and ()) of MyArray<T> are always marked const, and return references to T (hence, the const ensure safety, since MyArray<T>::operator[] returns T&, while MyArray<const T>::operator[] returns const T&).
Great. However, templates with different template arguments are non related, so I can't bind a reference to MyClass<T> to a reference of MyClass<const T>, meaning I can't do this
MyArray<double> ar(/*blah*/);
foo(ar);
Notice that, without a reference, the code above would work provided that there is a copy constructor that lets me create MyArray<const T> from MyArray<T>. However, I don't want to remove the reference, since the array construction would happen a lot of times, and, despite relatively cheap, its cost would add up.
So the question: how can I call foo with an MyArray<T>?
My only solution so far is the following:
MyArray<T> ar(/*blah*/);
foo(reinterpret_cast<MyArray<const T>>(ar));
(actually in my code I hid the reinterpret cast in an inlined function with more verbose name, but the end game is the same). The class MyArray does not have a specialization for const T that makes it not reinterpretable, so the cast should be 'safe'. But this is not really a nice solution to read. An alternative, would be to duplicate foo's signature, to have a version taking MyArray<T>, which implementation does the cast and calls the const version. The problem with this is code duplication (and I have quite a few functions foo that need to be duplicated).
Perhaps some extra template magic on the signature of foo? The goal is to pass both MyArray<T> and MyArray<const T>, while still retaining const-correctness (i.e., make the compiler bark if I accidentally change the input in the function body).
Edit 1: The class MyArray (whose implementation is not under my control), has const accessors, since it stores pointers. So calling v[3] will modify the values in the array, but not the members stored in the class (namely a pointer and some smart-pointer-like metadata). In other words, the object is de facto not modified by accessors, though the array is. It's a semantic distinction. Not sure why they went this direction (I have an idea, but too long to explain).
Edit 2: I accepted one of the two answers (though they were somewhat similar). I am not sure (for reasons long to explain) that the wrapper class is doable in my case (maybe, I have to think about it). I am also puzzled by the fact that, while
template<typename T>
void foo(const MyArray<const T>& x);
MyArray<int> a;
foo(a);
does not compile, the following does
void foo(const MyArray<const int>& x);
MyArray<int> a;
foo(a);
Note: MyArray does offer a templated "copy constructor" with signature
template<typename S>
MyArray(const MyArray<S>&);
so it can create MyArray<const T> from MyArray<T>. I am puzzled why it works when T is explicit, while it doesn't if T is a template parameter.
I would stay with
template<typename T>
void foo(const MyArray<T>&);
and make sure to instantiate it with const T (in unitTest for example).
Else you can create a view as std::span.
Something like (Depending of other methods provided by MyArray, you probably can do a better const view. I currently only used operator[]):
template <typename T>
struct MyArrayConstView
{
MyArrayConstView(MyArray<T>& array) : mArray(std::ref(array)) {}
MyArrayConstView(MyArray<const T>& array) : mArray(std::ref(array)) {}
const T& operator[](std::size_t i) {
return std::visit([i](const auto& a) -> const T& { return a[i]; }), mArray);
}
private:
std::variant<std::reference_wrapper<MyArray<T>>,
std::reference_wrapper<MyArray<const T>>> mArray;
};
and then
template <typename T>
void foo(const MyArrayConstView<T>&);
but you need to call it explicitly, (as deduction won't happen as MyArray<T> is not a MyArrayConstView)
MyArray<double> ar(/*blah*/);
foo(MyArrayConstView{ar});
foo<double>(ar);
Since you are not allowed to change MyArray, one option is to use an adapter class.
template <typename T>
class ConstMyArrayView {
public:
// Not an explicit constructor!
ConstMyArrayView(const MyArray<T>& a) : a_(a) {}
const T& operator[](size_t i) const { return a_[i]; }
private:
const MyArray<T>& a_;
};
template<typename T>
void foo(const ConstMyArrayView<T>& x);
MyArray<T> x;
foo(x);
But in the end, if you can change MyArray to match the const-correctness you want, or switch to a class that does, that'll be the better option.
Here's an ugly but effective way to have a function use one type, but also get the compiler to check that the same code would compile if it used a different type instead:
template <typename From, typename To>
struct xfer_refs_cv
{
using type = To;
};
template <typename From, typename To>
struct xfer_refs_cv<const From, To>
{
using type = const typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<volatile From, To>
{
using type = volatile typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<From&, To>
{
using type = typename xfer_refs_cv<From, To>::type&;
};
template <typename From, typename To>
struct xfer_refs_cv<From&&, To>
{
using type = typename xfer_refs_cv<From, To>::type&&;
};
template <typename CheckType, typename Func, typename CallType>
constexpr decltype(auto) check_and_call(Func&& f, CallType&& call_arg)
noexcept(noexcept(std::forward<Func>(f)(std::forward<CallType>(call_arg))))
{
(void) decltype(std::declval<Func&&>()
(std::declval<typename xfer_refs_cv<CallType&&, CheckType>::type>()), 0){};
return std::forward<Func>(f)(std::forward<CallType>(call_arg));
}
template<typename T>
void foo(const MyArray<T>& x)
{
check_and_call<MyArray<const T>>(
[](auto&& x) {
// Function implementation here.
}, x);
}

c++: why template cannot be used to deduce both container and element type?

I've got a very simple test program like below:
#include<vector>
#include<iostream>
using namespace std;
template<typename C, typename E>
void f(const C<E>& container){
cout<<container.size()<<endl;
}
int main(){
vector<int> i;
f(i);
return 0;
}
It fails to compile with gcc 4.1.2. Error message is:
templateContainer.cpp:5: error: ‘C’ is not a template
templateContainer.cpp: In function ‘int main()’:
templateContainer.cpp:10: error: no matching function for call to ‘f(std::vector<int, std::allocator<int> >&)’
You could use a template template parameter (and note that std::vector actually takes more than one template parameter [an element type, and an allocator type]).:
template<template <typename...> class C, typename... E>
void f(const C<E...>& container){
cout<<container.size()<<endl;
}
Live Demo
If you don't need the type decompositions, you could simply use an ordinary template.
template<typename C>
void f(const C& container){
cout<<container.size()<<endl;
}
You can additionally obtain typedefs from STL containers: for example, if you want to know the type of elements held by the container, value_type is there for you.
template<typename C>
void f(const C& container){
using ValueType = typename C::value_type;
cout<<container.size()<<endl;
}
std::vector has two template arguments, type and allocator.
template <template<class, class> class C, class E, class A>
void f(const C<E, A> &container)
{
std::cout << container.size() << endl;
}
int main()
{
std::vector<int> i;
f(i);
return 0;
}
Although WhiZTiM's answer is correct (well, preferring the second part), it doesn't explain why your code doesn't work.
Assuming for the moment that you intended roughly
template<template <typename> class C, typename E> void f(const C<E>&);
the reason that std::vector doesn't match is that it is the wrong shape - it has two type parameters, not one as in your declaration.
Just because you don't often explicitly write the defaulted second (allocator) param, doesn't mean it isn't there.
For comparison, this works (or doesn't) in an analogous way:
void f(int);
void g(int, int* = nullptr);
void apply(void (*func)(int), int);
apply(f, 42); // ok - f matches shape void(*)(int)
apply(g, 42); // error - g is really void(*)(int,int*)
specifically, default arguments (or type parameters) are syntactic sugar. They allow you to forget about those arguments at the call (instantiation) site, but don't change the shape of the function (or template).

Generic functions taking generic functions as arguments

I can't find a nice way to define generic higher-order functions taking generic functions as arguments. For example, take this attempt at one of the simplest such functions out there:
template<typename F, typename A>
auto apply(F f, const A& a) -> decltype(f(a)){return f(a);}
Of course it works as intended when used with non-template functions. But if I have, for instance
template<typename A>
A id(const A& a){return a;}
then
int a = 10;
int b = apply(id, a);
will not work, since id expects a template parameter. I could just write id<int> to make it work, but that sort of defeats the purpose (as I see it, it implies that if I wanted to write "filter" I'd have to write a separate definition for each generic predicate function). Using std::function or function pointers did not help. Also, I tried to make a "template template" version of apply, but I get various compiler errors when I try to use it:
template<template<typename> class F, typename A>
auto apply2(F<A> f, const A& a)-> decltype(f(a)){return f(a);}
The best I came up with was the following:
struct Id{
template<typename A>
static A func(const A& a){return a;}
};
template<typename F, typename A>
auto apply(A a)-> decltype(F::func(a)){return F::func(a);}
It's a bit ugly, but now at least I can actually parameterize by the function.
So, is there a better way to do generic functions taking generic functions as arguments?
This 'functor' works with your first version of apply.
struct id
{
template<typename A>
A operator ()(const A& a) {return a;}
};
later
int b = apply(id(), a); // with extra parenthesis to construct the struct.
In C++11 the best you can do is to use a struct with a templatized function call operator:
struct ID {
template <typename T>
void operator()(T arg) const {
...
}
};
The reason this is preferrable to a function [pointer] because it more likely to be inlined. With C++14 you could use lambdas:
[](auto arg){ ... }
which is just a cute version to write the struct above.