function template argument deduction with std::tuple

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

function template argument deduction with std::tuple

Boost - Users mailing list
Hi,

I have a problem related to function template argument deduction with
std::tuple (with boost::tuple too).  I attach a file with the complete
source you can compile. Below I describe the code.

I have a template function declaration:

 > template <typename T>
 > auto
 > get_cost(const T &);

Then I define my template types:

 > template <typename Graph>
 > using Vertex = typename Graph::vertex_descriptor;
 >
 > template <typename Graph>
 > using Label = std::tuple<Vertex<Graph>>;

I define the specialization of the get_cost function for the Label
type:

 > template <typename Graph>
 > auto
 > get_cost(const Label<Graph> &l)
 > {
 >   return std::get<0>(l);
 > }

I can define alright a variable of type Label:

 > struct graph
 > {
 >   using vertex_descriptor = unsigned;
 > };
 >
 > Label<graph> l;

But I cannot use the templated function:

 > get_cost(l);

I get this error:

 > error: use of 'auto get_cost(const T&) [with T =
 > std::tuple<unsigned int>]' before deduction of 'auto'

I got this error with GCC HEAD 8.0.0 20171113 and Clang HEAD 6.0.0 (I
used wandbox.org).

Some other type definitions and their specializations of the get_cost
function compile.  For a struct derived from the std::tuple:

 > template <typename Graph>
 > struct Label1: std::tuple<Vertex<Graph>>
 > {
 > };
 >
 > template <typename Graph>
 > auto
 > get_cost(const Label1<Graph> &l)
 > {
 >   return std::get<0>(l);
 > }

For a member of type std::tuple:

 > template <typename Graph>
 > struct Label2
 > {
 >   std::tuple<Vertex<Graph>> t;
 > };
 >
 > template <typename Graph>
 > auto
 > get_cost(const Label2<Graph> &l)
 > {
 >   return std::get<0>(l.t);
 > }

For a member of type Vertex<Graph>:

 > template <typename Graph>
 > struct Label3
 > {
 >   Vertex<Graph> m_v;
 > };
 >
 > template <typename Graph>
 > auto
 > get_cost(const Label3<Graph> &l)
 > {
 >   return l.m_v;
 > }

I would like to use the Label type, i.e., because it has the
constructor and operators already defined, which I need.

I would appreciate it if someone could explain what is going wrong
here.


Best,
Irek

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users

try.cc (1K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: function template argument deduction with std::tuple

Boost - Users mailing list
On 15/11/2017 20:55, Ireneusz Szcześniak wrote:
> I have a problem related to function template argument deduction with
> std::tuple (with boost::tuple too).  I attach a file with the complete
> source you can compile. Below I describe the code.
>
> I have a template function declaration:
>
>  > template <typename T>
>  > auto
>  > get_cost(const T &);
[...]
> But I cannot use the templated function:
>
>  > get_cost(l);
>
> I get this error:
>
>  > error: use of 'auto get_cost(const T&) [with T =
>  > std::tuple<unsigned int>]' before deduction of 'auto'

The problem is that you are never actually implementing the general
method, and thus the compiler can't infer what the return type should be.

You are providing several "better" overloads, but this first overload
still participates in overload resolution and the compiler doesn't like
that it has an undetermined return type.

The code compiles if you specify "void" as the return type for the above
method instead (or indeed pretty much anything except "auto"), or give
it an actual implementation before you try to call any of the overloads.

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: function template argument deduction with std::tuple

Boost - Users mailing list
Hi Gavin,

Thanks for your email.  However, I wonder why the other
specializations are used, and not the one for type Label.  If your
reasoning was correct, the code for other specializations (for Label1,
Label2, Label3) would fail to compile too.

My guess is that for some season a compiler does not consider the
specialization of get_cost for type Label, and reverts to the get_cost
declaration, thus complaining that the type for auto cannot be deduced
simply because there is no definition of the function.  There might be
some interplay between function overloading and function template
specialization.

I can be very specific and specialize the function this way:

 > template<>
 > auto
 > get_cost<>(const Label<graph> &l)
 > {
 >   return std::get<0>(l);
 > }

And the code compiles.

The example compiles also when I overload the function:

 > auto
 > get_cost(const Label<graph> &l)
 > {
 >   return std::get<0>(l);
 > }

But in that case I have to make sure the overload is defined before
any other template definition which uses this overload.

To illustrate what I mean, I'm attaching the same example as before,
but this time I added the template function callme, which uses
get_cost.  Right after the callme template, there is the
specialization definition of get_cost(const Label<graph> &l) with
which the example compiles even though that specialization comes after
the callme template definition.  If you comment out that
specialization of get_cost(const Label<graph> &l), and uncomment the
overload definition get_cost(const Label<graph> &l), the compilation
fails, because the overload should be defined before the callme
template definition (check it out, and move the overload of get_cost
before the callme template).

For that reason I would like to specialize the get_cost function
template, and not to overload the get_cost function.  And when I
specialize the get_cost function, I don't want to be too specific and
provide the definition for a single type (i.e., Label<graph>):

 > template<>
 > auto
 > get_cost(const Label<graph> &l)
 > {
 >   return std::get<0>(l);
 > }

But I want to be general, and provide a specialization template:

 > template <typename Graph>
 > auto
 > get_cost(const Label<Graph> &l)
 > {
 >   return std::get<0>(l);
 > }

That specialization above maybe should look something like this,
because it's a template of a complete specialization, but it doesn't
compile:

 > template <typename Graph>
 > template <>
 > auto
 > get_cost(const Label<Graph> &l)
 > {
 >   return std::get<0>(l);
 > }

So I guess I could conclude with a question: How can I make sure that
a compiler considers the specialization for type Label?


Best,
Irek

On 16.11.2017 06:18, Gavin Lambert via Boost-users wrote:

> On 15/11/2017 20:55, Ireneusz Szcześniak wrote:
>> I have a problem related to function template argument deduction with
>> std::tuple (with boost::tuple too).  I attach a file with the complete
>> source you can compile. Below I describe the code.
>>
>> I have a template function declaration:
>>
>>  > template <typename T>
>>  > auto
>>  > get_cost(const T &);
> [...]
>> But I cannot use the templated function:
>>
>>  > get_cost(l);
>>
>> I get this error:
>>
>>  > error: use of 'auto get_cost(const T&) [with T =
>>  > std::tuple<unsigned int>]' before deduction of 'auto'
>
> The problem is that you are never actually implementing the general
> method, and thus the compiler can't infer what the return type should be.
>
> You are providing several "better" overloads, but this first overload
> still participates in overload resolution and the compiler doesn't
> like that it has an undetermined return type.
>
> The code compiles if you specify "void" as the return type for the
> above method instead (or indeed pretty much anything except "auto"),
> or give it an actual implementation before you try to call any of the
> overloads.
>
> _______________________________________________
> Boost-users mailing list
> [hidden email]
> https://lists.boost.org/mailman/listinfo.cgi/boost-users

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users

try.cc (1K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: function template argument deduction with std::tuple

Boost - Users mailing list
On 16/11/2017 21:33, Ireneusz Szcześniak wrote:
> Thanks for your email.  However, I wonder why the other specializations
> are used, and not the one for type Label.  If your reasoning was
> correct, the code for other specializations (for Label1, Label2, Label3)
> would fail to compile too.

In your original post, you don't have any specialisations -- just
overloads.  They are not the same thing.

> My guess is that for some season a compiler does not consider the
> specialization of get_cost for type Label, and reverts to the get_cost
> declaration, thus complaining that the type for auto cannot be deduced
> simply because there is no definition of the function.  There might be
> some interplay between function overloading and function template
> specialization.

That's true; I didn't try linking so didn't notice that it was indeed
still selecting the T overload instead of the Label<Graph> overload.

I think the issue here is that as Label is an alias, the compiler has
trouble deducing things about it:

   1. To the compiler, "l" is a variable with type std::tuple<unsigned>.
   2. When calling get_cost, it has to choose between several overloads
-- the Label1/2/3 overloads can obviously be excluded, because
std::tuple is not those types.  So the choice is between T and
std::tuple<Vertex<Graph>>.
   3. Vertex<Graph> is also an alias, though, so that is actually
std::tuple<Graph::vertex_descriptor>.
   4. When matching std::tuple<unsigned> to
std::tuple<Graph::vertex_descriptor>, it does not know what type Graph
is and it cannot infer it from the parameter alone, so cannot resolve
what Graph::vertex_descriptor might be.
   5. So it must choose T.

Note that the existence of the T overload doesn't influence this -- if
you comment it out then it will still fail because it still can't deduce
what Graph should be purely from the arguments.


The Label1/2/3 overloads work because the parameter type is actually
Label1<graph> (a real type, not just an alias), and so when matching
const Label1<Graph>& it is able to deduce that Graph=>graph.  Once it
knows that then it can find graph::vertex_descriptor.


You can make it work by forgoing the deduction and specifying the Graph
type explicitly:

     get_cost<graph>(l);

This forces it to exclude the generic T overload because l isn't a const
graph&, so it will match the const Label<graph>& overload instead.

Another option is to pass a Graph object as an additional parameter
directly; this will allow its type to be deduced and then it will be
able to determine that Graph::vertex_descriptor is an unsigned, and
match the overload.

Still another option is to make Vertex a "real" type; this will make the
parameter std::tuple<Vertex<graph>> and again it will be able to match
this by deducing Graph.

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: function template argument deduction with std::tuple

Boost - Users mailing list
Gavin, thank you for your insightful answer!  I think I understand
your arguments why the template argument deduction failed.  I changed
my implementation, so that my type is derived from std::tuple.  For
now I'm satisfied.

However, it bugs me that I'm still not sure whether I've implemented a
function-template specialization or a template for function
overloading.  You claim that this is an overload:

> template <typename Graph>
> auto
> get_cost(const Label1<Graph> &l)
> {
>   return std::get<0>(l);
> }

Could you please elaborate on how to tell one from the other?  Had it
been a specialization, a compiler should accept the explicit template
argument:

> template <typename Graph>
> auto
> get_cost<Label1<Graph>>(const Label1<Graph> &l)
> {
>   return std::get<0>(l);
> }

but I get (with gcc version 7.2.0):

> error: non-class, non-variable partial specialization
> ‘get_cost<Label1<Graph> >’ is not allowed
> get_cost<Label1<Graph>>(const Label1<Graph> &l)

So it seems that I was defining overloads (these don't have template
arguments), not specializations as I thought.  The C++ Programming
Language, 4th edition, the bottom of page 737, says that you can drop
the explicit template argument in the definition of a template
specialization, if it can be deduced.  So I dropped the explicit
template argument, and still considered the definition the specialization.

I have a hard time understanding the above error message, thought.  I
understand that "non-class" simply means that it's not a member
function, OK.  But what "non-variable partial specialization" could
possibly mean?  What is "non-variable" here, and why "partial
specialization" when I gave all (i.e., one) template argument.  I
consider it a complete specialization albeit dependent on a template
parameter Graph.

On 17.11.2017 00:12, Gavin Lambert via Boost-users wrote:

> On 16/11/2017 21:33, Ireneusz Szcześniak wrote:
>> Thanks for your email.  However, I wonder why the other
>> specializations are used, and not the one for type Label.  If your
>> reasoning was correct, the code for other specializations (for
>> Label1, Label2, Label3) would fail to compile too.
>
> In your original post, you don't have any specialisations -- just
> overloads.  They are not the same thing.
>
>> My guess is that for some season a compiler does not consider the
>> specialization of get_cost for type Label, and reverts to the
>> get_cost declaration, thus complaining that the type for auto cannot
>> be deduced simply because there is no definition of the function.  
>> There might be some interplay between function overloading and
>> function template specialization.
>
> That's true; I didn't try linking so didn't notice that it was indeed
> still selecting the T overload instead of the Label<Graph> overload.
>
> I think the issue here is that as Label is an alias, the compiler has
> trouble deducing things about it:
>
>    1. To the compiler, "l" is a variable with type std::tuple<unsigned>.
>    2. When calling get_cost, it has to choose between several
> overloads -- the Label1/2/3 overloads can obviously be excluded,
> because std::tuple is not those types.  So the choice is between T and
> std::tuple<Vertex<Graph>>.
>    3. Vertex<Graph> is also an alias, though, so that is actually
> std::tuple<Graph::vertex_descriptor>.
>    4. When matching std::tuple<unsigned> to
> std::tuple<Graph::vertex_descriptor>, it does not know what type Graph
> is and it cannot infer it from the parameter alone, so cannot resolve
> what Graph::vertex_descriptor might be.
>    5. So it must choose T.
>
> Note that the existence of the T overload doesn't influence this -- if
> you comment it out then it will still fail because it still can't
> deduce what Graph should be purely from the arguments.
>
>
> The Label1/2/3 overloads work because the parameter type is actually
> Label1<graph> (a real type, not just an alias), and so when matching
> const Label1<Graph>& it is able to deduce that Graph=>graph.  Once it
> knows that then it can find graph::vertex_descriptor.
>
>
> You can make it work by forgoing the deduction and specifying the
> Graph type explicitly:
>
>      get_cost<graph>(l);
>
> This forces it to exclude the generic T overload because l isn't a
> const graph&, so it will match the const Label<graph>& overload instead.
>
> Another option is to pass a Graph object as an additional parameter
> directly; this will allow its type to be deduced and then it will be
> able to determine that Graph::vertex_descriptor is an unsigned, and
> match the overload.
>
> Still another option is to make Vertex a "real" type; this will make
> the parameter std::tuple<Vertex<graph>> and again it will be able to
> match this by deducing Graph.
>
> _______________________________________________
> Boost-users mailing list
> [hidden email]
> https://lists.boost.org/mailman/listinfo.cgi/boost-users

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: function template argument deduction with std::tuple

Boost - Users mailing list
On 23/11/2017 01:11, Ireneusz Szcześniak wrote:

> However, it bugs me that I'm still not sure whether I've implemented a
> function-template specialization or a template for function
> overloading.  You claim that this is an overload:
>
>> template <typename Graph>
>> auto
>> get_cost(const Label1<Graph> &l)
>> {
>>   return std::get<0>(l);
>> }
>
> Could you please elaborate on how to tell one from the other?  Had it
> been a specialization, a compiler should accept the explicit template
> argument:
>
>> template <typename Graph>
>> auto
>> get_cost<Label1<Graph>>(const Label1<Graph> &l)
>> {
>>   return std::get<0>(l);
>> }
>
> but I get (with gcc version 7.2.0):
>
>> error: non-class, non-variable partial specialization
>> ‘get_cost<Label1<Graph> >’ is not allowed
>> get_cost<Label1<Graph>>(const Label1<Graph> &l)
>
> So it seems that I was defining overloads (these don't have template
> arguments), not specializations as I thought.  The C++ Programming
> Language, 4th edition, the bottom of page 737, says that you can drop
> the explicit template argument in the definition of a template
> specialization, if it can be deduced.  So I dropped the explicit
> template argument, and still considered the definition the specialization.
>
> I have a hard time understanding the above error message, thought.  I
> understand that "non-class" simply means that it's not a member
> function, OK.  But what "non-variable partial specialization" could
> possibly mean?  What is "non-variable" here, and why "partial
> specialization" when I gave all (i.e., one) template argument.  I
> consider it a complete specialization albeit dependent on a template
> parameter Graph.

I suggest reading http://www.gotw.ca/publications/mill17.htm.

The short version is that function templates are a bit weird, and if you
have anything other than template<> it's an overload, because they don't
do partial specialization.

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: function template argument deduction with std::tuple

Boost - Users mailing list
Gavin, thank you again for your time and expertise!  Thanks for the
reference!

One way to verify I was defining overloads is to remove the template
function declaration.  After removing the declaration, the code
compiles, without complaining that the declaration is missing, which
is necessary for specialization.

On 23.11.2017 06:11, Gavin Lambert via Boost-users wrote:

> On 23/11/2017 01:11, Ireneusz Szcześniak wrote:
>> However, it bugs me that I'm still not sure whether I've implemented
>> a function-template specialization or a template for function
>> overloading.  You claim that this is an overload:
>>
>>> template <typename Graph>
>>> auto
>>> get_cost(const Label1<Graph> &l)
>>> {
>>>   return std::get<0>(l);
>>> }
>>
>> Could you please elaborate on how to tell one from the other?  Had
>> it been a specialization, a compiler should accept the explicit
>> template argument:
>>
>>> template <typename Graph>
>>> auto
>>> get_cost<Label1<Graph>>(const Label1<Graph> &l)
>>> {
>>>   return std::get<0>(l);
>>> }
>>
>> but I get (with gcc version 7.2.0):
>>
>>> error: non-class, non-variable partial specialization
>>> ‘get_cost<Label1<Graph> >’ is not allowed
>>> get_cost<Label1<Graph>>(const Label1<Graph> &l)
>>
>> So it seems that I was defining overloads (these don't have template
>> arguments), not specializations as I thought.  The C++ Programming
>> Language, 4th edition, the bottom of page 737, says that you can
>> drop the explicit template argument in the definition of a template
>> specialization, if it can be deduced.  So I dropped the explicit
>> template argument, and still considered the definition the
>> specialization.
>>
>> I have a hard time understanding the above error message, thought.  
>> I understand that "non-class" simply means that it's not a member
>> function, OK.  But what "non-variable partial specialization" could
>> possibly mean?  What is "non-variable" here, and why "partial
>> specialization" when I gave all (i.e., one) template argument.  I
>> consider it a complete specialization albeit dependent on a template
>> parameter Graph.
>
> I suggest reading http://www.gotw.ca/publications/mill17.htm.
>
> The short version is that function templates are a bit weird, and if
> you have anything other than template<> it's an overload, because they
> don't do partial specialization.
>
> _______________________________________________
> Boost-users mailing list
> [hidden email]
> https://lists.boost.org/mailman/listinfo.cgi/boost-users

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users