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)...);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With