Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

variadic templates, decltype, sfinae (maybe)...issue

125 views
Skip to first unread message

nroberts

unread,
May 8, 2012, 4:39:00 AM5/8/12
to
So I'm trying to make something sort of like a tuple that is accessed
with a placeholder variable using variadic templates. I keep getting
an error I do not expect:


#include <utility>

template < int I > struct arg {};
template < typename ... Pack> struct thingy;

template < typename Head, typename ... Tail>
struct thingy<Head,Tail...> : thingy<Tail...>
{
Head val;

thingy(Head && h, Tail&& ... t)
: thingy<Tail...>(std::forward<Tail>(t)...), val(h)
{}

template < int I >
auto get(arg<I>) -> decltype(thingy<Tail...>::get(arg<I-1>()))
{
return thingy<Tail...>::get(arg<I-1>());
}

Head& get(arg<1>) { return val; }
};
template < > struct thingy<> {};

arg<1> _1;
arg<2> _2;

#include <iostream>
int main() {
thingy<int,double> t(5,23.4);

std::cout << t.get(_1) << std::endl;
}


I expect here for t.get(_1) to use the arg<1> overload but it seems to
instead be choosing the templated version. If that is commented out,
I get my expected behavior, but I want that function to recurse into
get(arg<1>).

The only thing I can think of is that SFINAE doesn't apply as I expect
it here. I'd expect that the template overload of get makes itself
unavailable when it is determined that it causes a syntax error due to
trying to access the get function in thingy<>, where it doesn't
exist. When I make the function outside of the template that is what
I get and the recursive get indeed resolves into an overload for
arg<1>:

template < typename Head, typename ... Tail >
Head& get(thingy<Head, Tail...> & b, arg<1>)
{
return b.value;
}
template < int I, typename Head, typename ... Tail >
auto get(thingy<Head,Tail...> & b, arg<I>) ->
decltype(get(static_cast<thingy<Tail...>&>(b), arg<I-1>()))
{
return get(static_cast<thingy<Tail...>&>(b), arg<I-1>());
}

Is what I'm trying to do with the first one supposed to not work? If
so, what's the rule I'm missing that makes it so?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

cpplj...@gmail.com

unread,
May 8, 2012, 9:13:44 AM5/8/12
to
On Tuesday, May 8, 2012 3:39:00 AM UTC-5, nroberts wrote:
> So I'm trying to make something sort of like a tuple that is
> accessed with a placeholder variable using variadic templates. I
> keep getting an error I do not expect:
>
>
> #include <utility>
>
> template < int I > struct arg {};
> template < typename ... Pack> struct thingy;
>
> template < typename Head, typename ... Tail>
> struct thingy<Head,Tail...> : thingy<Tail...>
> {
> Head val;
[snip]
> };
> template < > struct thingy<> {};
[snip]
You need to add a using declaration for the super type's
get. This also means you need a dummy get for the
empty tuple, as follows:

template < typename Head, typename ... Tail>
struct thingy<Head,Tail...> : thingy<Tail...>
{
using thingy<Tail...>::get;
};
template < > struct thingy<>
{
void get(){}
};

HTH.
-regards,
Larry

nroberts

unread,
May 10, 2012, 2:58:25 PM5/10/12
to
On May 8, 6:13 am, cppljev...@gmail.com wrote:
> On Tuesday, May 8, 2012 3:39:00 AM UTC-5, nroberts wrote:
> > So I'm trying to make something sort of like a tuple that is
> > accessed with a placeholder variable using variadic templates. I
> > keep getting an error I do not expect:
>
> > #include <utility>
>
> > template < int I > struct arg {};
> > template < typename ... Pack> struct thingy;
>
> > template < typename Head, typename ... Tail>
> > struct thingy<Head,Tail...> : thingy<Tail...>
> > {
> > Head val;
> [snip]
> > };
> > template < > struct thingy<> {};
>
> [snip]
> You need to add a using declaration for the super type's
> get. This also means you need a dummy get for the
> empty tuple, as follows:
>
> template < typename Head, typename ... Tail>
> struct thingy<Head,Tail...> : thingy<Tail...>
> {
> using thingy<Tail...>::get;};
>
> template < > struct thingy<>
> {
> void get(){}
>
> };

Well, I was rather confused by this answer, I wouldn't expect to need
to say 'using' when I'm explicitly describing scope, but I tried it
anyway. It did not work. It allows the _1 case to compile, but as
soon as I try to use _2 it blows up claiming there's no arg<2> version
of the function. It also complains that the get in thingy<> doesn't
take any arguments but one was provided.


#include <utility>

template < int I >
struct arg {};

template < typename ... Pack >
struct thingy;

template < typename Head, typename ... Tail >
struct thingy<Head, Tail...> : thingy<Tail...>
{
Head value;

thingy(Head && h, Tail && ... t)
: thingy<Tail...>(std::forward<Tail>(t)...)
, value(h)
{}

using thingy<Tail...>::get;

Head& get(arg<1>) { return value; }

template < int I >
auto get(arg<I>) -> decltype(thingy<Tail...>::get(arg<I-1>()))
{
return thingy<Tail...>::get(arg<I-1>());
}

};

template <>
struct thingy<> {
void get() {}
};

arg<1> _1;
arg<2> _2;
arg<3> _3;

#include <iostream>
int main()
{
thingy<int,char> args(5,'c');

std::cout << "Int: " << args.get(_1) << std::endl;
std::cout << "Char: " << args.get(_2) << std::endl;
}



**** Build of configuration Debug for project bind0 ****

**** Internal Builder is used for build ****
g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -o main.o ..
\main.cpp
..\main.cpp: In function 'int main()':
..\main.cpp:57:38: error: no matching function for call to
'thingy<int, char>::get(arg<2>&)'
..\main.cpp:57:38: note: candidates are:
..\main.cpp:21:8: note: Head& thingy<Head, Tail ...>::get(arg<1>)
[with Head = int, Tail = {char}]
..\main.cpp:21:8: note: no known conversion for argument 1 from
'arg<2>' to 'arg<1>'
..\main.cpp:24:63: note: template<int I> decltype
(thingy<Tail ...>::get(arg<(I - 1)>())) thingy<Head,
Tail ...>::get(arg<I>) [with int I = I, Head = int, Tail = {char},
decltype (thingy<Tail ...>::get(arg<(I - 1)>())) = decltype
(thingy<>::get(arg<(I - 1)>()))]
..\main.cpp:33:7: note: void thingy<>::get()
..\main.cpp:33:7: note: candidate expects 0 arguments, 1 provided
..\main.cpp:24:63: note: template<int I> decltype
(thingy<Tail ...>::get(arg<(I - 1)>())) thingy<Head,
Tail ...>::get(arg<I>) [with int I = I, Head = char, Tail = {},
decltype (thingy<Tail ...>::get(arg<(I - 1)>())) = decltype (arg<(I -
1)>()->thingy<>::get())]
Build error occurred, build is stopped
Time consumed: 356 ms.

Marc

unread,
May 10, 2012, 7:22:38 PM5/10/12
to
nroberts wrote:

> So I'm trying to make something sort of like a tuple that is accessed
> with a placeholder variable using variadic templates. I keep getting
> an error I do not expect:
>
>
> #include <utility>
>
> template < int I > struct arg {};
> template < typename ... Pack> struct thingy;
>
> template < typename Head, typename ... Tail>
> struct thingy<Head,Tail...> : thingy<Tail...>
> {
> Head val;
>
> thingy(Head && h, Tail&& ... t)
> : thingy<Tail...>(std::forward<Tail>(t)...), val(h)
> {}
>
> template < int I >
> auto get(arg<I>) -> decltype(thingy<Tail...>::get(arg<I-1>()))
> {
> return thingy<Tail...>::get(arg<I-1>());
> }
>
> Head& get(arg<1>) { return val; }
> };
> template < > struct thingy<> {};

This looks a lot like:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51501

You need to find another way to specify the return type of the
function, which shouldn't be hard in this case.

cpplj...@gmail.com

unread,
May 11, 2012, 8:40:49 AM5/11/12
to
On Thursday, May 10, 2012 1:58:25 PM UTC-5, nroberts wrote:
> On May 8, 6:13 am, cppljev...@gmail.com wrote:
> > On Tuesday, May 8, 2012 3:39:00 AM UTC-5, nroberts wrote:
> > > So I'm trying to make something sort of like a tuple that is
> > > accessed with a placeholder variable using variadic templates.
[snip]
> > You need to add a using declaration for the super type's
> > get.
[snip]
> Well, I was rather confused by this answer, I wouldn't expect to need
> to say 'using' when I'm explicitly describing scope, but I tried it
> anyway. It did not work. It allows the _1 case to compile, but as
> soon as I try to use _2 it blows up claiming there's no arg<2> version
> of the function.
[snip]
I was wrong about the using declaration; however,
I think the problem is with the decltype argument.
It uses a member function without an object.
Try:
---cut here---
#include <utility>
template < int I >
auto get(arg<I>) -> decltype(std::declval<thingy<Tail...>
>().get(arg<I-1>()))
{
return thingy<Tail...>::get(arg<I-1>());
}

---cut here---
Also, you still need the dummy get in the empty tuple.
[snip]
HTH

-regards,
Larry

nroberts

unread,
May 11, 2012, 9:31:10 PM5/11/12
to
On May 11, 5:40 am, cppljev...@gmail.com wrote:

> I was wrong about the using declaration; however,
> I think the problem is with the decltype argument.
> It uses a member function without an object.
> Try:
> ---cut here---
> #include <utility>
> template < int I >
> auto get(arg<I>) -> decltype(std::declval<thingy<Tail...>>().get(arg<I-1>()))
>
> {
> return thingy<Tail...>::get(arg<I-1>());
>
> }
>
> ---cut here---
> Also, you still need the dummy get in the empty tuple.

Finally got a chance to try this.

It seems that you're probably right. Once you mentioned it I was
hitting myself for not knowing already.

Unfortunately, the compiler now vomits with: "sorry, unimplemented:
mangling overload"

Not sure what that means...but I would take it as a sign that it's
supposed to work.

Thanks for the help! Looks like I need to wait a bit to do what I'm
trying to do...or upgrade the compiler if possible.
0 new messages