Wrong pack expanded in variadic template - c++

I got a very strange problem with variadic templates. It seems the wrong pack is expanded. Here's a code snippet:
#include <tuple>
template<typename...>
struct types {};
template<typename = types<>>
struct Base;
template<typename... Args1>
struct Base<types<Args1...>> {
template<typename... Args2>
static auto construct(Args1... args1, Args2&&... args2)
-> decltype(std::make_tuple(args1.forward()..., std::declval<Args2>()...))
{
return std::make_tuple(args1.forward()..., std::forward<Args2>(args2)...);
}
};
struct Derived : Base<> {};
int main() {
auto test = &Derived::construct<char const(&)[7]>;
}
I get this error:
13 : <source>:13:43: error: request for member 'forward' in 'args2#0', which is of non-class type 'const char [7]'
-> decltype(std::make_tuple(args1.forward()..., std::declval<Args2>()...))
~~~~~~^~~~~~~
13 : <source>:13:43: error: request for member 'forward' in 'args2#0', which is of non-class type 'const char [7]'
<source>: In function 'int main()':
22 : <source>:22:27: error: unable to deduce 'auto' from '& construct<const char (&)[7]>'
auto test = &Derived::construct<char const(&)[7]>;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
22 : <source>:22:27: note: could not resolve address from overloaded function '& construct<const char (&)[7]>'
Compiler exited with result code 1
However, it don't happen when the pack has values in it:
struct HasForward { int forward() { return 0; } };
struct Derived : Base<types<HasForward>> {};
Here's the First snippet live and the Second snippet live
What's wrong with this code? Is this a compiler bug? Is there any ways to overcome it and leave the first pack empty?

Is this a compiler bug? Is there any ways to overcome it and leave the first pack empty?
It looks like a bug in your compiler.
To work around it you can use a function declaration (no definition required) like the one in the following example and use it to test your parameters:
template<typename... Args1>
class Base<types<Args1...>> {
template<typename... T, typename... U>
static auto ret(types<T...>, types<U...>)
-> decltype(std::make_tuple(std::declval<T>().forward()..., std::declval<U>()...));
public:
template<typename... Args2>
static auto construct(Args1... args1, Args2&&... args2)
-> decltype(ret(types<Args1...>{}, types<Args2...>{}))
{
return std::make_tuple(args1.forward()..., std::forward<Args2>(args2)...);
}
};
A bit ugly, but it works when your first pack is empty (also in C++11 as requested) and everything should be discarded by the linker.
--- EDIT
As suggested by #W.F. in the comments (thanks for the suggestion, I didn't notice it), it's even easier to accomplish that.
Just define your function as it follows:
static auto construct(Args1... args1, Args2&&... args2)
-> decltype(std::make_tuple(std::declval<Args1>().forward()..., std::declval<Args2>()...))
{
return std::make_tuple(args1.forward()..., std::forward<Args2>(args2)...);
}

Related

Why auto is not allowed in template function for creating a built-in array?

This code below does not compile:
template<typename... Ts>
void CreateArr(const Ts&... args)
{
auto arr[sizeof...(args) + 1]{ args... };
}
int main()
{
CreateArr(1, 2, 3);
}
due to the following errors:
'arr': in a direct-list-initialization context, the type for 'auto [6]' can only be deduced from a single initializer expression
auto [6]': an array cannot have an element type that contains 'auto'
'initializing': cannot convert from 'const int' to 'std::initializer_list<int>'
My questions are:
Why cannot I use auto to define the type of the array?
How to define it properly to work with the template?
Why cannot I use auto to define the type of the array?
For the same reason, following does not work/ allowed!
auto ele[]{ 1, 2, 3 };
More reads: Why can't I create an array of automatic variables?
How to define it properly to work with the template?
Use the std::common_type_t for specifying the type
#include <type_traits> // std::common_type_t
template<typename... Ts>
void CreateArr(const Ts&... args)
{
std::common_type_t<Ts...> arr[sizeof...(args)]{ args... };
static_assert(std::is_array_v<int[sizeof...(args)]>, "is not array!");
}
(See a Live Demo)

Taking Eigen::Vector as a parameter

I am trying to write a function that takes an Eigen::Vector<T, dim> as a parameter. However, the following example fails to compile:
#include <Eigen/Core>
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Vector<T, dim>& arg1) {
}
template<typename T, int dim>
void foo(const Eigen::Vector<T, dim>& a) {
return bar([] {}, a);
}
int main() {
Eigen::Vector<float, 3> v1{ 1.f,2.f,3.f };
foo(v1);
return 0;
}
This, under Visual Studio 2019, gives me the following error:
1>main.cpp(9,10): error C2672: 'bar': no matching overloaded function found
1>main.cpp(14): message : see reference to function template instantiation 'void foo<float,3>(const Eigen::Matrix<float,3,1,0,3,1> &)' being compiled
1>main.cpp(9,1): error C2784: 'void bar(F &&,const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &)': could not deduce template argument for 'const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &' from 'const Eigen::Matrix<float,3,1,0,3,1>'
1>main.cpp(4): message : see declaration of 'bar'
My questions:
What is this weird |_Rows==&&?:&&_Rows!=?: in the error message?
What can I do to make the above code compile?
The bar function should have T and dim availabe. I cannot just take const AnyType& arg1, because the actual implementation of bar depends on compile-time known values T and dim.
I have seen https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html. I think I understand what they are saying, but I am not sure if it applies here. I am taking an actual Eigen::Vector as an argument, not an expression.
If there was an expression it would be fine for me, to have it materialized.
Nevertheless, if I try to follow their instruction and just use ArrayBase<Derived>, I lose the compile-time information about T and dim.
This indeed looks like an MSVC issue, it compiles fine with gcc >= 4.7, and clang >= 3.5: https://godbolt.org/z/kqoHyO
One possible workaround would be to explicitly write out what Eigen::Vector expands to:
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Matrix<T, dim, 1, 0, dim, 1>& arg1) {
}
https://godbolt.org/z/vlvSDP
The weird |_Rows==&&?:&&_Rows!=?: looks like MSVC mangled the default value of the Options template parameter:
AutoAlign |
( (_Rows==1 && _Cols!=1) ? Eigen::RowMajor
: (_Cols==1 && _Rows!=1) ? Eigen::ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION ),
If you want to get to the bottom of this, you should file a bug-report to the MSVC maintainers, maybe using a simplified example like this: https://godbolt.org/z/U_0Sh7 (probably it's possible to reduce this even more).

Excluding member by type of first template parameter

I build with VS2013 compiler these member functions:
template<typename R, typename ...Args>
fc::variant memberfoo( const std::function<R(Args...)>& f, variants::const_iterator a0, variants::const_iterator e )const
{
return fc::variant(f(Args...)); // error C2440: '<function-style-cast>' : cannot convert from 'void' to 'fc::variant'
}
template<typename ...Args>
fc::variant memberfoo(const std::function<void(Args...)>& f, variants::const_iterator a0, variants::const_iterator e)const
{
f(Args... );
return fc::variant();
}
I think that reason of error is that compiler does not chose member by current template type of parameter "std::function< void(Args...) > &" and in this case he select first memberfoo.
How to help compiler to chose member by type of std::function< some_function_declaration > ? Or some other tip how to adjust code to be compilable ?

Detecting parameter types from generic lambda - compile error with GCC

I wrote some code that retrieves the types of the non-auto parameters when given a generic lambda function. As you can see in the code below, the idea is to call the connect function with a generic lambda and provide arguments for the auto parameters (which will always be at the front in my use case). So in the code below my goal was to detect that the second parameter is of type float.
The code works fine with clang 3.8 but it doesn't compile with gcc 6.1.1, so I was wondering whether this was a bug in gcc or if this is just not valid c++ code? Can I assume that a generic lambda is implemented with a templated operator() function or is this compiler-specific?
template <typename Functor, typename... AllArgs, typename... ProvidedArgs>
void findArgTypes(void(Functor::*)(AllArgs...) const, Functor, ProvidedArgs...)
{
// AllArgs == int, float
// ProvidedArgs == int
}
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
}
int main()
{
int tmp = 0;
connect([&](auto, float){ ++tmp; }, 0);
}
The error that gcc gives is this:
main.cpp: In instantiation of ‘void connect(Func, ProvidedArgs ...) [with Func = main()::<lambda(auto:1, float)>; ProvidedArgs = {int}]’:
main.cpp:16:33: required from here
main.cpp:11:17: error: no matches converting function ‘operator()’ to type ‘void (struct main()::<lambda(auto:1, float)>::*)() const’
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:27: note: candidate is: template<class auto:1> main()::<lambda(auto:1, float)>
connect([](auto, float){}, 0);
^
Removing the const in findArgTypes gives the same result.
Using the following code works with both compilers:
struct Foo
{
template <typename T>
void operator()(T, float) const {}
};
int main()
{
Foo f;
connect(f, 0);
}
You have error because you are expecting functor (object) but lambda with empty capture is convertible to free function:
int main() {
using function = void (*)(int, float);
function a = [](auto, float){};
}
See lambda from cppreference:
For the newest version of your question that implementation satisfies both compilers:
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
auto mf = &Func::template operator()<ProvidedArgs...>;
findArgTypes(mf, func, providedArgs...);
}
I think this is gcc compiler bug that gcc needs this auto local variable to work correctly...
BTW, one question - one bug in clang, one in gcc - I really advice you to find simpler way to achieve your goals - maybe consider to just use std::function instead of quite fresh generic-lambda?

Constructing class with member tuple containing rvalue references

When trying to construct a class which is supposed to hold a tuple created by calling std::forward_as_tuple I ran into the following error when compiling with clang(187537) and libc++:
/usr/include/c++/v1/tuple:329:11: error: rvalue reference to type 'int' cannot
bind to lvalue of type 'int'
: value(__t.get())
^ ~~~~~~~~~
/usr/include/c++/v1/tuple:447:8: note: in instantiation of member function
'std::__1::__tuple_leaf<0, int &&, false>::__tuple_leaf' requested here
struct __tuple_impl<__tuple_indices<_Indx...>, _Tp...>
^
tuple.cpp:31:5: note: in instantiation of function template specialization
'make_foo2<int>' requested here
make_foo2(1 + 1);
^
In file included from tuple.cpp:2:
/usr/include/c++/v1/tuple:330:10: error: static_assert failed "Can not copy a
tuple with rvalue reference member"
{static_assert(!is_rvalue_reference<_Hp>::value, "Can not copy ...
I was able to work around the above error by declaring the return type differently, but, from my understanding, it should have the same semantics so I would not expect it to stop the error. In the below code make_foo is the workaround which does not error out and make_foo2 causes the above error. I am able to successfully compile both versions using gcc 4.8.1 and the version of clang at coliru.
#include <utility>
#include <tuple>
template<class Tuple>
struct foo
{
Tuple t;
foo(Tuple &&t) : t(std::move(t)) { }
};
template<class... Args>
using ForwardedTuple = decltype(std::forward_as_tuple(std::forward<Args>(std::declval<Args>())...));
template<class... Args>
foo<ForwardedTuple<Args...>> make_foo(Args&&... args)
{
return {std::forward_as_tuple(std::forward<Args>(args)...)};
}
template<class... Args>
auto make_foo2(Args&& ...args) ->
decltype(foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...)))
{
return foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...));
}
int main()
{
make_foo(1 + 1);
make_foo2(1 + 1);
}
What is the difference between the above make_foo functions and is make_foo2 incorrect?
Thanks.
Looks like you return foo<> from make_foo2. But foo doesn't have move constructor generated (Compiler won't generate it). Therefore copy constructor is called and compilation fails because of that.