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
(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
’ is not allowed get_cost (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
. 3. Vertex<Graph> is also an alias, though, so that is actually std::tupleGraph::vertex_descriptor. 4. When matching std::tuple<unsigned> to std::tupleGraph::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
and again it will be able to match this by deducing Graph. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users