Boost logo

Boost :

Subject: Re: [boost] Variadic append for std::string
From: Richard Hodges (hodges.r_at_[hidden])
Date: 2017-01-26 09:04:09


>
> Template specialisations of free functions are always a bad idea - they
> don't play nicely with ADL. So I would not recommend
> convert<std::string>(join(...)) etc.
>

> Please elaborate more on that. I can't see it.

imagine:

namespace boost
{
  // the concept
  template<class To>
  T convert(joiner const& j);

  // some specialisations
  template<> std::string convert<std::string>(joiner const& j) {... }

  template<> std::wstring convert<std::wstring>(joiner const& j) {... }
};

then in user code:

template<class T>
void do_something(boost::joiner const& j)
{
  using boost::convert;
  auto v = something(convert<T>(j));
  something_else(v);
}

now someone wishes to provide their own converter, not in the boost
namespace:

namespace user {

  struct UserRepresentation;

  // this is not allowed. There is not already a general template called
user::convert<>. It's called boost::convert<>
  template<>
  UserRepresentation convert<UserRepresentation>(boost::joiner const& j) {
... }
}

As mentioned in the comments, this is not allowed. So the user is not
forced to specialise the boost namespace. This is not the boost way (see
boost::hash), and for good reason. Namespaces are for separation. This
forces crowding of the boost namespace. Also, specialising templates in
foreign namespaces is a source of user confusion. Google mentioned this in
their proposal to make std::hash behave more like boost::hash (std::hash
being an example of the standards committee turning a fantastic tool into
an incomplete shambles, because they left out the bits that make it work
well).

If you want a template convert function (and I don't, but I can imagine
that some might), then the model to follow would be that of boost::hash<>

This uses a function object (boost::hash) which then calls out through a
namespace collector and finally to the ADL hash_value function. In our case
it would want to call out to a function like convert(boost::tag<T>,
boost::joiner const& j) -> T. The general form would be:

namespace boost {
  template<class T>
  struct join_converter {
    decltype(auto) operator()(tag<T>, joiner const& j) const {
      using boost::convert
    }
  };
}

The signature of the general converter would then be

  auto convert(boost::tag<T>, joiner const& j) -> T

and the user's non-template overload would be:

namespace user {

  auto convert(boost::tag<UserRepresentation>, boost::joiner const& j) ->
UserRepresentation;

}

boost would never define a convert function. Conversions for std::string,
std::wstring etc would be specialisations of boost::join_converter.

This allows ADL to find non-template converters in namespaces associated
with the thing that they are converting to.

The generic user calling function would then look more like this:

template<class T>
void do_something(boost::joiner const& j)
{
  auto convert = boost::converter<T>();
  auto v = convert(something(j));
  something_else(v);
}

I'm afraid that this "class template which calls free function" dance is
necessary to allow calls to ADL free functions to search beyond the boost
namespace.

Again, see boost::hash for the gory details.

On 26 January 2017 at 14:27, Christof Donat <cd_at_[hidden]> wrote:

> Hi,
>
> Am 26.01.2017 10:47, schrieb Richard Hodges:
>
>> I don't see any particular reason why you can't provide all of them,
>>> though (even the implicit conversion), as different things are going to
>>> feel more natural to different people, particularly in different
>>> contexts.
>>>
>>
>> There's a problem with implicit conversion.
>>
>> imagine the overloads function:
>>
>> void foo(std::string const& s);
>>
> [...]
>
>> foo(join(...));
>>
> [...]
>
>> void foo(std::string_view s);
>> void foo(const char* s);
>>
>> Now the above code will not compile, because there are 3 ambiguous
>> overloads.
>>
>
> But that is only an issue for users, who have relied on the implicit
> conversion upfront. You can always use an implicit conversion explicitly as
> well, of course, and implicit conversion will be just one of multiple
> options, if we add it.
>
> The moral of the story is that if we are going to provide conversion
>> operators, they need to be explicit anyway.
>>
>
> Up to now, you haven't made a point here. Just those users, who decide to
> rely on implicit conversion, instead of one of the explicit variants, have
> to cope with the downsides of implicit conversion. Others don't. I think
> that fits well with "don't pay for what you don't use".
>
> Template specialisations of free functions are always a bad idea - they
>> don't play nicely with ADL. So I would not recommend
>> convert<std::string>(join(...)) etc.
>>
>
> Please elaborate more on that. I can't see it.
>
> Christof
>
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman
> /listinfo.cgi/boost
>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk