C++17 Partial Deduction Guide - c++

I am trying to write a deduction guide, that only detects one of many typename's from given constructor argument and requires user to enter int size manually
template <int size, typename T>
struct Board
{
array<array<T, size>, size> values;
explicit Board(const vector<T>& raw_values){
}
};
template <int size, typename T> Board(const vector<T>&) -> Board<int size, T>;
The idea above is that user should still be forced to enter "int size" argument of template, but "typename T" should be deduced from the argument of constructor, is this possible?
After correct specification, this is how method should be called
auto b = Board<3>(initialStateVector);
Currently, it requires to me to enter like this;
auto b = Board<3, int>(initialStateVector);
So basically, I want "int" above to be deduced from given initialStateVector, which has type
const vector<int>& raw_values

The idea above is that user should still be forced to enter "int size" argument of template, but "typename T" should be deduced from the argument of constructor, is this possible?
According a note (and following examples) in this cppreference page
Class template argument deduction is only performed if no template argument list is present. If a template argument list is specified, deduction does not take place.
no, this isn't possible (not in C++17; we can hope in future versions of the standard).
If you want explicit the size and let deduce the type, the best I can imagine is pass through a good-old make_something function.
I mean something as follows (using std::size_t for the size, as in std::array and almost all STL)
template <std::size_t S, typename T>
Board<S, T> make_Board (std::vector<T> const & v)
{ return {v}; }
// ...
auto b = make_Board<3>(initialStateVector);
that should works also in C++11.

I came up with a workaround using a size hint object
template<int size>
struct SizeHint {};
Your class would take this as an additional constructor argument:
Board(SizeHint<size>, const std::vector<T>& raw_values)
You call the constructor like this:
auto b = Board(SizeHint<2>{}, v);
Bonus
This approach also works for type hints (my original motivation how I found this thread):
template<typename T>
struct TypeHint{};
template<typename Result, typename T>
struct S {
S(TypeHint<Result>, T arg) : t{arg}{}
Result r() {return t;}
T t;
};
#include <iostream>
int main() {
S s{TypeHint<int>{}, 5.7};
std::cout << s.r() << std::endl;
}
This can also be used in combination with variadic templates:
template<typename Result, typename... Args>
struct S {
S(TypeHint<Result>, Args... args) : t{args...}{}
std::tuple<Args...> t;
};

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

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.

Is there a way to use variable template as a parameter?

I want to declare a function, which will take as a parameter a variable (let's say, int), which should be parametrized by a class. Speaking in terms of lambda calculus, I want my parameter to have a kind * -> int.
Example of a function I want to be able to write (Spec is the variable):
template <??? Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
Since C++14 we have variable templates, so we can do something like this:
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
The problem is, how do I pass that into a function? In the notes section of cppreference it is stated that
Variable templates cannot be used as template template arguments.
But does that mean that there is actually no way to pass parametrized variable as a function parameter? What you can do is, for example, create a class which has a static field denoting value, but an obvious drawback is that the users of my function must derive from that class.
I believe there might be some workaround using SFINAE, but I lack skills in that area.
Unless you insist on using a variable template, you can use a type trait:
template <typename T> struct Specification;
you can specialize it for example for int:
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
and as you want to have different Specifications, pass it as template template parameter:
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
Complete example:
#include <array>
#include <cstddef>
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
template <typename T> struct Specification;
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
int main(){
auto x = make_array<Specification,int>();
}
Note that I was rather verbose for the sake of clarity. You can save a bit of typing by using std::integral_constant, eg:
template <>
struct Specification<double> : std::integral_constant<size_t,3> {};
As an follow-up on idclev's answer, you can avoid the need to explicitly specialise for the different types for individual "specifications" just by inheriting from e.g. integral_constant. For example, your desired usage was something like
template <template <typename T> int Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
// ...
auto foo = make_array<digits, double>();
However, as you have noted, this is impossible: you cannot pass variable templates as template-template parameters. However, by turning digits into a structure directly you can do this:
template <template <typename T> class Specification, typename T>
auto make_array() {
// we no longer have the guarantee of the type here, unfortunately
// but you can use a static_assert to improve error messages
// (also using `std::size_t` here for correctness)
static_assert(
std::is_convertible<decltype(Specification<T>::value), std::size_t>::value,
"value must be convertible to size_t");
return std::array<T, Specification<T>::value>;
}
// use a type rather than a variable template
// we just inherit from integral constant to save on some typing
// (though you could do this explicitly as well)
template <typename T>
struct digits : std::integral_constant<int, std::numeric_limits<T>::digits> {};
// ...
// same call!
auto foo = make_array<digits, double>();

Template array length as argument

I am trying to learn more about templates in C++. I would like to be able to call a function where I pass it a type and the length as an argument. Is this possible?
template <class T>
void alloc_arr (int l) {
std::allocator<T[l]> a;
}
alloc_arr<int[]>(64);
It doesn't work because the instantiated type must be fixed at compile time (T[l] is not fixed).
Is there some other way to do this which doesn't require the length to be specified in the type (<T[64]>)?
Is there some other way to do this which doesn't require the length to be specified in the type ()?
In some way, you need to pass it as template parameter
You can pass it explicitly, as suggested by Lourens Dijkstra
template <typename T, std::size_t Dim>
void alloc_arr ()
{
std::allocator<T[Dim]> a;
// ...
}
or, if you can use at least C++11, also you can deduce it from the type of an argument; by example,
template <typename T, std::size_t Dim>
void alloc_arr (std::integral_constant<std::size_t, Dim> const &)
{
std::allocator<T[Dim]> a;
// ...
}
or also
template <typename T, typename U>
void alloc_arr (U const &)
{
std::allocator<T[U::value]> a;
// ...
}
calling alloc_arr with a std::integral_constant<std::size_t, 5u>{}, by example.
You could pass the size as a template parameter:
template <class T, size_t size>
void alloc_arr() { ... }
This is the only way. A couple of days ago I found out that passing a constexpr lambda as a regular parameter is considered ill-formed: Trying to pass a constexpr lambda and use it to explicitly specify returning type
Also, note that type T should be int; not int[].
So, calling alloc_arr:
alloc_arr<int, 64>();

trailing return type of deduction guide is not a specialization

I'm trying to do an advanced class template argument deduction by using the new deduction guides from c++17. Unfortunately, it looks like you can only use simple template declarations after the ->, but I need a helper struct to determine the resulting type.
My use case is this one: I have a variadic template class that takes an arbitrary amount of different types. For one constructor I want to specify every single one, for another ctor I want to specify only one type and replicate it N times.
To access this N in the deduction guide I introduced a new type:
template<size_t N>
struct Replicate { };
The class I have is similar this one:
template<typename... Foos>
struct Foo {
// [ ... ] member std::tuple<Foos...>
// ctor 1: give values for all types (easy to deduce)
Foo(Foos&&... args /* rvalue ref, NOT forwarding */) { };
// ctor 2: give one value and N, result is N copies of this value.
// takes Replicate<N> as parameter to aid the deduction.
template<typename T, size_t N>
Foo(const T& t, Replicate<N>) { };
};
The usage would be like this:
Foo f1{1, 2, 3.0}; // deduce Foo<int, int, double>;
Foo f2{8, Replicate<4>{}}; // deduce Foo<int, int, int, int>;
The deduction guide for the first one is straight forward:
template<typename... Ts>
Foo(Ts&&...) -> Foo<Ts...>;
It gets problematic with the second (ctor 2) deduction guide. First I need a helper struct to create Foo<T, T, ... /* total N times */, T> from T and N.
template<typename, typename, typename>
struct ExpandNTimes;
template<typename T, size_t... Is>
struct ExpandNTimes<T, std::index_sequence<Is...>> {
template<size_t> using NthType = T;
using Type = Foo<NthType<Is>...>;
};
Then in the deduction guide I want to utilize the helper to deduce the correct type. I cant directly use Foo<something> as there is no kind of "in place parameter pack creation", therefore the helper struct.
template<typename T, size_t N>
Foo(const T&, Replicate<N>) -> typename ExpandNTimes<T, std::make_index_sequence<N>>::Type;
Unfortunately this results int an error similar to this one:
error: trailing return type of 'typename ExpandNTimes<T, /* std things */>::Type' deduction guide is not a specialization of ‘Foo<Ts>’
Is there any way to work around this issue?
This is impossible with class template argument deduction - both template names must be the same, and the thing after the -> must be a simple-template-id. This doesn't leave any room for template shenanigans.
But nothing prevents you from doing the thing that class template argument deduction is intended to replace: factory functions:
template <typename T, size_t N>
typename ExpandNTimes<T, std::make_index_sequence<N>>::Type
makeFoo(T const&, Repliace<N>);
This is doable if you can change Replicate's definition to embed a pack into a base class:
template<class> struct ReplicateBase {};
template<size_t N> struct Replicate : ReplicateBase<std::make_index_sequence<N>> {};
template<size_t, class T> using Meow = T;
template<typename T, size_t... Ns>
Foo(const T&, ReplicateBase<std::index_sequence<Ns...>>) -> Foo<Meow<Ns, T>...>;
Then it's a "simple" matter of constraining everything else to not compete with this guide when passed a Replicate:
Foo(Foos&&... args) { } and template<typename... Ts> Foo(Ts&&...) -> Foo<Ts...>; (are you sure you want to deduce references when passed lvalues?) should be constrained to when Foos/Ts aren't Replicates
template<typename T, size_t N> Foo(const T& t, Replicate<N>); needs to be constrained to prevent it from being used to deduce an empty pack (e.g., to when sizeof...(Foos) == N)