Skip to content

Ambiguity between tag_invoke and others #28

@Lastique

Description

@Lastique

It looks like the user-provided tag_invoke overload creates ambiguity if the type also matches other categories of types supported by Boost.Hash2. For example:

#include <iostream>
#include <boost/describe.hpp>
#include <boost/hash2/fnv1a.hpp>
#include <boost/hash2/hash_append.hpp>

struct X
{
    int a;

    template< typename Hash, typename Flavor >
    friend void tag_invoke(boost::hash2::hash_append_tag const&,
        Hash& h, Flavor const& f, X const& v)
    {
        std::cout << "Called my tag_invoke" << std::endl;
        boost::hash2::hash_append(h, f, v.a);
    }
};

BOOST_DESCRIBE_STRUCT(X, (), (a))

int main()
{
    boost::hash2::fnv1a_32 h1;
    X v1 = { 1 };
    boost::hash2::hash_append( h1, {}, v1 );
}

Gcc 13.2 output:

In file included from test_tag_invoke.cpp:4:
./include/boost/hash2/hash_append.hpp: In instantiation of ‘constexpr void boost::hash2::hash_append(Hash&, const Flavor&, const T&) [with Hash = fnv1a_32; Flavor = default_flavor; T = X]’:
test_tag_invoke.cpp:25:30:   required from here
./include/boost/hash2/hash_append.hpp:419:31: error: call of overloaded ‘do_hash_append(boost::hash2::fnv1a_32&, const boost::hash2::default_flavor&, const X&)’ is ambiguous
  419 |         detail::do_hash_append( h, f, v );
      |         ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
./include/boost/hash2/hash_append.hpp:357:5: note: candidate: ‘constexpr typename std::enable_if<boost::container_hash::is_described_class<T>::value, void>::type boost::hash2::detail::do_hash_append(Hash&, const Flavor&, const T&) [with Hash = boost::hash2::fnv1a_32; Flavor = boost::hash2::default_flavor; T = X; typename std::enable_if<boost::container_hash::is_described_class<T>::value, void>::type = void]’
  357 |     do_hash_append( Hash& h, Flavor const& f, T const& v )
      |     ^~~~~~~~~~~~~~
./include/boost/hash2/hash_append.hpp:401:5: note: candidate: ‘constexpr typename std::enable_if<boost::hash2::detail::has_tag_invoke<T>::value, void>::type boost::hash2::detail::do_hash_append(Hash&, const Flavor&, const T&) [with Hash = boost::hash2::fnv1a_32; Flavor = boost::hash2::default_flavor; T = X; typename std::enable_if<has_tag_invoke<T>::value, void>::type = void]’
  401 |     do_hash_append( Hash& h, Flavor const& f, T const& v )
      |     ^~~~~~~~~~~~~~

I think, a user-provided tag_invoke should always be preferred even if the type matches one of the natively supported type category. The code above is expected to compile and output "Called my tag_invoke".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions