Boost logo

Boost Users :

Subject: Re: [Boost-users] function template argument deduction with std::tuple
From: Ireneusz Szcześniak (irek.szczesniak_at_[hidden])
Date: 2017-11-22 12:11:40


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
> Boost-users_at_[hidden]
> https://lists.boost.org/mailman/listinfo.cgi/boost-users


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net