c++ 如何检测过载解析失败?

nfzehxib  于 2022-11-27  发布在  其他
关注(0)|答案(1)|浏览(150)

请考虑以下代码片段:

#include <tuple>
#include <type_traits>
#include <utility>
#include <cstddef>

template <typename ...>
struct always_false : std::false_type {};

template <>
struct always_false<decltype([] {})> : std::true_type {};

template <typename ...Ts>
inline constexpr bool always_false_v = always_false<Ts...>::value;

template <typename>
struct type_wrapper {};

template <std::size_t I, typename T>
struct tuple_index_impl_base
{
    auto foo(type_wrapper<T>) -> std::integral_constant<std::size_t, I>;
};

template <typename ...Ts>
struct tuple_index_impl_base_fallback
{
    auto foo(...)
    {
        static_assert(always_false_v<Ts...>, "Type not exist.");
    }
};

template <typename, typename ...>
struct tuple_index_impl;

template <std::size_t ...Is, typename ...Ts>
struct tuple_index_impl<std::index_sequence<Is...>, Ts...> : tuple_index_impl_base<Is, Ts>..., tuple_index_impl_base_fallback<Ts...>
{
    using tuple_index_impl_base<Is, Ts>::foo...;
    using tuple_index_impl_base_fallback<Ts...>::foo;
};

template <typename, typename>
struct tuple_index;

template <typename T, typename ...Ts>
struct tuple_index<T, std::tuple<Ts...>> : decltype(std::declval<tuple_index_impl<std::make_index_sequence<std::tuple_size_v<std::tuple<Ts...>>>, Ts...>>().foo(std::declval<type_wrapper<T>>())) {};

template <typename T, typename U>
inline constexpr std::size_t tuple_index_v = tuple_index<T, U>::value;

它实现一个元函数tuple_index来获取与std::tuple中的类型相对应的索引。
如果类型存在并且在元组中只存在一次,则该方法可以正常工作:

constexpr std::size_t i = tuple_index_v<char&, std::tuple<int, float, char, char&, const char*, char>>;    // i == 3

而如果该类型不存在,则static_assert会被触发并打印一条错误消息:

constexpr std::size_t i = tuple_index_v<long, std::tuple<int, float, char, char&, const char*, char>>;    //  error C2338: static_assert failed: 'Type not exist.'

live demo)(英文)
但是,如果该类型存在多次,编译器将开始比较ambiguous call to overloaded function

constexpr std::size_t i = tuple_index_v<char, std::tuple<int, float, char, char&, const char*, char>>;    //  error C2668: 'tuple_index_impl_base_fallback<int,float,char,char &,const char *,char>::foo': ambiguous call to overloaded function

live demo
如果重载集中有两个相同的函数,编译器似乎不会选择回退函数(在本例中为tuple_index_impl_base_fallback<Ts...>::foo(...))。
如何在这种情况下检测过载解析失败并使用static_assert捕获它?

gr8qqesn

gr8qqesn1#

您可以使用requires运算式检查多载解析是否会成功:

template <std::size_t ...Is, typename ...Ts>
struct tuple_index_impl<std::index_sequence<Is...>, Ts...> : tuple_index_impl_base<Is, Ts>..., tuple_index_impl_base_fallback<Ts...>
{
    using tuple_index_impl_base<Is, Ts>::foo...;
    using tuple_index_impl_base_fallback<Ts...>::foo;

    template<typename T>
    static auto foo2() {
        using wrapper = type_wrapper<T>;
        static_assert(requires(tuple_index_impl i, wrapper w) { i.foo(w); }, "Duplicate type");
        return tuple_index_impl{}.foo(wrapper{});
    }
};

template <typename T, typename ...Ts>
struct tuple_index<T, std::tuple<Ts...>> : decltype(tuple_index_impl<std::make_index_sequence<std::tuple_size_v<std::tuple<Ts...>>>, Ts...>::template foo2<T>()) {};

相关问题