Unable to deduce 'auto' from 'tuple_cat' - c++

Base Problem
The base problem I'm trying to solve is this:
I have a template parameter pack ArgTypes and I need to make a tuple with each of the types wrapped in std::optional. For example: make_optional_tuple<int, double, std::string> should return a tuple of tuple of type std::tuple<std::optional<int>, std::optional<double>, std::optional<std::string>> with each element in the tuple initialized to std::nullopt.
My work so far
I'm using g++ included with GCC 7.1. I've been wrestling with the C++ type system for quite a while and I have code that works with one type but not multiple types. The error I get with multiple types in the parameter pack is:
error: unable to deduce 'auto' from 'tuple_cat<std::make_tuple(_Elements&& ...) [with _Elements = {std::optional<int>&}](), optional_tuple>'
Does anyone know how I can fix this? Intuitively (though I may be incorrect) I think the problem is that the C++ type system is unable to deduce the type of auto optional_tuple as it involves fully resolving the chain of recursions of the different function templates produced by the parameter pack -- something that perhaps the type system is unable to do when it is trying to resolve the type of auto variables.
Here is a minimal working example:
#include <optional>
#include <tuple>
template<int n, typename ArgType, typename... ArgTypes>
struct make_optional_tuple_helper {
static auto tuple() {
std::optional<ArgType> optional_arg = {};
auto optional_tuple = make_optional_tuple_helper<n-1, ArgTypes...>::tuple();
return std::tuple_cat<std::make_tuple(optional_arg), optional_tuple>;
}
};
template<typename ArgType>
struct make_optional_tuple_helper<1, ArgType> {
static std::tuple<std::optional<ArgType>> tuple() {
std::optional<ArgType> optional_arg = {};
return std::make_tuple(optional_arg);
}
};
template<typename... ArgTypes>
auto make_optional_tuple() {
return make_optional_tuple_helper<std::tuple_size<std::tuple<ArgTypes...>>::value, ArgTypes...>::tuple();
};
int main() {
auto i = make_optional_tuple<int>(); // works!
auto j = make_optional_tuple<int, double>(); // error: unable to deduce 'auto'...
}
(Compile with g++-7 -std=c++1z example.cpp)
Thanks for your time and/or help!

You are way overthinking this:
template<typename... ArgTypes>
auto make_optional_tuple() {
return std::tuple<std::optional<ArgTypes>...>{};
}
Since a default-constructed optional is nullopt, that's all you need.
Your specific problem is you used the wrong brackets:
return std::tuple_cat<std::make_tuple(optional_arg), optional_tuple>;
~~~ ~~~
Those should be parentheses. As-is, you're returning a pointer to an ill-formed function template specialization instead of a tuple.

It will not work because the return type of the function is deduced on the first return, and you are trying to call the function before the first return.
I believe you could do something long the lines of:
template<typename... ArgTypes>
struct make_optional_tuple_helper {
static auto tuple() {
return std::make_tuple(std::optional<ArgTypes>()...);
}
};
Or
template<typename... ArgTypes>
struct make_optional_tuple_helper {
static auto tuple() {
return std::tuple<std::optional<ArgTypes>...>();
}
};

Related

How do I define a class template with specfic format of taking template arguments? e.g.: Fn(Args...)

template<class Fn, class ...Args>
class func_class<Fn(Args...)> // Do not know what to do here
{
typedef typename result_of<Fn(Args...)>::type mytype;
std::function<mytype(Args...)> func_;
std::tuple<Args...> tuple1;
public:
func_class(Fn&& func_in, Args&& ...args)
{
func_ = func_in;
tuple1 = make_tuple(args...);
}
mytype
exe ()
{
mytype ret;
ret = apply(func_, tuple1);
return ret;
}
};
int func(int a) {return a;}
int main () {
// return type of "func" can be deduced by "result_of<decltype(func)&(int)>::type"
// And result_of is declared as "result_of<Fn(Args...)>"
// Want func_class to have the same interface
func_class<decltype(func)&(int)> fc; // Want to declare a object like this
fc.exe();
}
The code is like above. Return type of func can be deduced by result_of<decltype(func)&(int)>::type. And result_of is declared as result_of<Fn(Args...)>.
Want func_class to have the same interface as result_of.
The compiler complains like:
test.cpp:211:7: error: 'func_class' is not a class template
What can I do?
Thank you in advance.
template<class Sig>
struct bob;
template<class R, class...Args>
struct bob<R(Args...)>{
//...
};
specialization.
Uses of bob that fail to pattern match will give compile-time errors.
As a note, using R(Args...) syntax when R is not a return value and Args... are not arguments will lead to unexpected quirks, because of how function argument and return value types are modified by the C/C++ language.
This is why std::result_of<F(Args...)> is deprecated and replaced with std::invoke_result<F, Args...>.
R(Args...) is appropriately used in std::function, because Args... are function arguments, and R is an actual return value, to function<A(Args...)>::operator().

Failing to work around g++ 7.1 structured binding bug with Boost.Bimap

In my project I am using Boost.Bimap to implement bidirectional maps.
Look at this very simple MCVE on godbolt, where I am using structured binding to print the key-value pair of the right map (which, according to the documentation, is signature-compatible to std::map.
Problem
It compiles fine for any g++ version >= 7.4 and later, however I need to use g++ 7.1. and here this code fails with the following message:
<source>: In function 'int main()':
<source>:11:20: error: 'std::tuple_size<const boost::bimaps::relation::structured_pair<boost::bimaps::tags::tagged<const long unsigned int, boost::bimaps::relation::member_at::right>, boost::bimaps::tags::tagged<const std::__cxx11::basic_string<char>, boost::bimaps::relation::member_at::left>, mpl_::na, boost::bimaps::relation::mirror_layout>>::value' is not an integral constant expression
for (const auto& [key, value] : bm.right) {
I was able to find out that this is due to a bug in g++ that seems to have been fixed in later versions.
Workaround attempt (toy example, successful)
In order to make the structured bindings work with my compiler version, I attempted to create a workaround by specializing std::tuple_size, std::tuple_element and std::get. See this cppreference link for more information.
For simplicity, I successfully tried this first with a toy structure. Here are the specializations, check out the full code on godbolt.org:
struct SampleType {
int a = 42;
std::string b = "foo"s;
double c = 3.141;
};
#if (__GNUC__ == 7) && (__GNUC_MINOR__ == 1)
template <std::size_t N>
decltype(auto) get(const ::SampleType& t) {
if constexpr (N==0) return t.a;
else if constexpr (N==1) return t.b;
else return t.c;
}
namespace std {
// Tuple size is 3
template <> struct tuple_size<::SampleType> : std::integral_constant<std::size_t, 3> {};
// Define tuple types
template <std::size_t N> struct tuple_element<N, ::SampleType> {
// Deduce type from get() function template defined above
using type = decltype(::get<N>(std::declval<::SampleType>()));
};
}
#endif
Note that if you remove the #ifdef for g++ 7.1., the compilation will fail with the same error as above (...is not an integral constant expression). (Interesting: Unlike the boost::bimap example, which only compiles fine with g++ 7.4 onwards, the toy example already succeeds with g++ 7.2)
Workaround attempt (original example, not successful)
Now, being very convinced that I found the solution, I made an attempt to do the same for boost::bimap but I am failing helplessly (check it out on godbolt.org):
template <std::size_t N>
decltype(auto) get(const bimap::right_map::value_type& bm) {
if constexpr (N==0) return bm.first;
else if constexpr (N==1) return bm.second;
}
namespace std {
// Tuple size is 2 -> key-value pair
template <> struct tuple_size<bimap::right_map::value_type> : std::integral_constant<std::size_t, 2> {};
// Define tuple types
template <> struct tuple_element<0, bimap::right_map::value_type> { using type = std::string; };
template <> struct tuple_element<1, bimap::right_map::value_type> { using type = std::size_t; };
}
The error message is too long to post here (see godbolt output), but basically I understand that the overload for "my" get is not being matched by the compiler. Note that for debugging reasons I have inserted the following line into my code to make sure that I am actually dealing with the correct type in my specializations.
for (const auto& pair : bm.right) {
// Make sure we capture the right type in the specializations above
static_assert(std::is_same<
decltype(pair),
const bimap::right_map::value_type&
>::value);
}
Am I doing something wrong? Or is this bug posing an insurmountable obstactle to my workaround attempt?
I don't think this is something you can work around.
Here's a shorter reproduction:
#include <tuple>
namespace N {
struct X {
template <typename T> void get() { }
};
}
namespace std {
template <> struct tuple_size<N::X> : integral_constant<size_t, 1> { };
template <> struct tuple_element<0, N::X> { using type = int; };
}
namespace N {
template <size_t I> decltype(auto) get(X const&) { return 42; }
}
int main() {
auto [i] = N::X{};
}
This is a valid program. The wording from [dcl.struct.bind]/4 says, emphasis mine:
The unqualified-id get is looked up in the scope of E by class member access lookup ([basic.lookup.classref]), and if that finds at least one declaration that is a function template whose first template parameter is a non-type parameter, the initializer is e.get<i>(). Otherwise, the initializer is get<i>(e), where get is looked up in the associated namespaces ([basic.lookup.argdep]).
The fact that N::X has a member function template get() that takes a type template parameter should cause us to then consider ADL lookup on get, which should find the non-member N::get. gcc 7.4 does this correctly, gcc 7.3 complains about N::X::get() not working.
The only way to work around that is to wrap the initializer somehow. Basically do something like:
auto [i] = wrap(N::X{});
Where wrap returns some new type that definitely doesn't have a member named get, so that you can provide the non-member you want. I'm not sure if there's a solution here that doesn't require additional wrapping. Besides just using gcc 7.4 :-)

C++ / variadic template & parameter pack using decltype(lambda)

I am sorry to ask a question that was may be asked before, but I searched long enough and did not find an answer.
My problem is that I would like to call a template function of type template <typename GenericLambda, typename... LambdaArgs> without the need to always write decltype(my_lambda) first in the template parameters.
#include <functional>
#include <string>
template <typename GenericLambda, typename... LambdaArgs>
auto lambda_to_mem_fn() {
auto ptr = &GenericLambda::template operator() < LambdaArgs... > ;
auto as_mem_fn = std::mem_fn(ptr);
return as_mem_fn;
}
auto my_lambda = [](auto x, auto y) { return x + y; };
// imaginary function
template <????>
auto make_lambda_to_mem_fn(GenericLambda generic_lambda)
{
// Extract lambda args types and forward them to lambda_to_mem_fn
using GenericLambda = decltype(generic_lambda);
lambda_to_mem_fn<GenericLambda, LambdaArgs...>();
}
void test() {
// How to remove the need to write decltype(my_lambda) ?
auto as_mem_fn = lambda_to_mem_fn<decltype(my_lambda), int, int>;
// I would like to write:
auto as_mem_fn2 = make_lambda_to_mem_fn<int, int>(my_lambda);
}
I need a solution that is portable (i.e works on gcc, clang and msvc).
I'm scratching my head since quite some time in this, and would appreciate some help ;-)
Link to compiler explorer snippet:
https://godbolt.org/z/pFk09J
The restriction where a parameter pack (if present) must be the final parameter in a template parameter list applies only to primary class templates. If the type can be deduced or defaulted, it can appear after the parameter pack:
template <typename... LambdaArgs, typename GenericLambda>
auto make_lambda_to_mem_fn(GenericLambda generic_lambda)
{
return lambda_to_mem_fn<GenericLambda, LambdaArgs...>();
}
Any template arguments will be consumed by LambdaArgs, while GenericLambda will be deduced from the argument expression.

Error deducing variadic function template

I'm having a problem with the type deduction of variadic template functions. Here is what I'm trying to do:
I have a template class which works as an allocator. In order to choose the right allocator, I need some information about the type parameters. The information is collected in a vector.
The functions creating the actual meta data look like this:
template<typename T>
MetaData* create_meta() {
return new DefaultMetaData();
}
template<MyExampleType>
MetaData* create_meta() {
return new MyExampleTypeMetaData();
}
template<MyOtherType>
MetaData* create_meta() {
etc.
}
And the function collecting the meta data looks like this right now:
template<typename T, typename... Args>
void fill_meta_data(std::vector<MetaData*> &meta) {
meta.push_back(create_meta<T>());
fill_meta_data<Args...>(meta);
}
edit: Trying to clearify the problem:
I want to call the fill_meta_data function like this:
fill_meta_data<MyExampleType, MyExampleType, MyOtherType>(meta_data_vector)
And as a result the meta_data_vector to contain Meta Data for MyExampleType, MyExampleType and MyOtherType
The error I'm getting is "template argument deduction/substitution failed: couldn't deduce template parameter 'T'".
I think that the problem occurs when it tries to deduce the argument for the no-arg version, however, I just can't figure out how to define this default (where it should just return).
I already tried template<> (which is not recognized), template<typename... Args> (the compiler says there are two implementations for more than 0 parameters).
Different solutions for the problem would also be welcome :)
Thanks!
EDIT: Thanks to #catscradle's link:
Here is a solution which worked for me:
I had to add the template
template<typename... Args>
typename std::enable_if<sizeof...(Args) == 0>::type
fill_meta_data(std::vector<MetaData*> &meta) {}
which is only enabled when the size of the Args parameters is zero.
Thanks everybody!
There's a few syntax errors in your code, but there shouldn't be a problem once you sort them out.
Your function specializations are wrong. Instead of:
template<typename T>
MetaData* create_meta() {
return new DefaultMetaData();
}
template<MyExampleType>
MetaData* create_meta() {
return new MyExampleTypeMetaData();
}
Try:
template<typename T>
MetaData* create_meta() {
return new DefaultMetaData();
}
template <>
MetaData* create_meta<MyExampleType>() {
return new MyExampleTypeMetaData();
}
The other issue is that your recursion doesn't have a final function. Some find it helpful to draw out recursion so it makes more sense. If we draw out your recursion with some types, we might get this:
fill_meta_data<short, int, long> // T = short, Args... = int, long
fill_meta_data<int, long> // T = int, Args... = long
fill_meta_data<long> // T = long, Args... =
fill_meta_data<> // T = ???, Args... =
You can see that the final step is undefined because T has no meaning, but it needs an input. So, to "close off" your recursive template function you'll just need a regular function overload with no arguments that does nothing:
void fill_meta_data(std::vector<MetaData*> &meta) {
}
template<typename T, typename... Args>
void fill_meta_data(std::vector<MetaData*> &meta) {
meta.push_back(create_meta<T>());
fill_meta_data<Args...>(meta);
}

Type deduction and argument passing with variadic template templates

I implemented a C++ equivalent of Python's chain function a while ago thanks to variadic templates. The function is used to iterate successively through many containers. Here is the old working version of the function using a generator named ChainedObject, whatever it is:
template<typename... Iterables>
auto chain(Iterables&&... iters)
-> ChainObject<Iterables...>
{
return /* ... */;
}
And the corresponding main:
int main()
{
std::vector<int> vec = { 1, 2, 3, 4, 5 };
std::list<int> li = { 6, 7, 8, 9, 10, 11, 12, 13 };
for (auto& i: chain(vec, li))
{
// You can edit a range of iterables
// as if there was only one of them.
i *= 5;
std::cout << i << std::endl;
}
return 0;
}
That main worked fine. We don't care what there is in ChainObject for the problem, so let's see it. I tried to use template templates to ensure that the different collections used had the same value_type and modified the function chain the following way:
template<typename T, template<typename...> class... Iterables>
auto chain(Iterables<T>&&... iters)
-> ChainObject<T, Iterables...>
{
return /* ... */;
}
I thought this would do the trick to ensure the list and vector from my previous main share a same type, but instead, I get the following error from GCC 4.7.1:
In function 'int main()':
error: no matching function for call to 'chain(std::vector&, std::list&)'
note: candidates are:
note: ChainObject<T, Iterables ...> chain(Iterables<T>&& ...) [with T = int; Iterables = {std::vector, std::list}]
note: no known conversion for argument 2 from 'std::list<int>' to 'std::list<int>&&'
note: ChainObject<T, Iterables ...> chain(Iterables<T>&& ...) [with T = int; Iterables = {std::vector, std::list}]
note: no known conversion for argument 2 from 'std::list<int>' to 'std::list<int>&&'
error: unable to deduce 'auto&' from ''
It seems that the problem comes from argument passing to the function taking rvalue references. However, I really don't understand why my first version worked fine, and note the one using template templates.
Your problem is that the T&& template magic only works for type parameters (it works by deducing T as eg. int& if needed - for lvalue arguments). It can't work for template template arguments, where the actual type is X<T>&& - X must be a class template in this case, not something like "reference-to-class-template". So in the end you have to pass a rvalue-reference, which you cannot implicitly get from a lvalue (variable).
That said, I would suggest you to revert to your earlier code and check that the value_types are the same (or compatible, etc., whatever gets you going) with SFINAE.
Rough code sketch (for strict equality):
template <class ... Ts> struct all_types_equal
{
static const bool value = false;
};
template <class T>
struct all_types_equal<T>
{
static const bool value = true;
};
template <class T, class ... Rest>
struct all_types_equal<T, T, Rest...>
{
static const bool value = all_types_equal<T, Rest...>::value;
};
template<typename... Iterables>
auto chain(Iterables&&... iters)
-> typename std::enable_if<all_types_equal<Iterable::value_type...>::value, ChainObject<Iterables...> >::type