将 std::enable_if 与 constexpr 参数一起用于重载解析无法按预期工作

Using std::enable_if with constexpr argument for overload resolution doesn't work as expected

我正在尝试根据其中一个模板参数的类型为函数模板添加函数重载,并在其他地方传递定义为 conexpr 的参数以帮助重载解析。

这是我要实现的目标的代码示例:

struct use_my_impl_t {};
constexpr use_my_impl_t use_my_impl;

template<
        typename T,
        typename std::enable_if<std::is_same<T, use_my_impl_t>::value>::type * = nullptr>
void foo(int i, T&& t)
{
    std::cout << "Using my impl: " << --i << std::endl;
}

template<
    typename T,
    typename std::enable_if<!std::is_same<T, use_my_impl_t>::value>::type * = nullptr>
    void foo(int i, T&& t)
{
    std::cout << "Using default impl: " << ++i << std::endl;
}

因此,当我将 use_my_impl_t 的实例作为第二个参数传递时,它应该使用我的实现,而对于所有其他情况,它应该使用默认实现。结果不是我所期望的。如果我这样调用:

constexpr use_my_impl_t use_my_impl;

foo(42, use_my_impl_t{});
foo(42, 2);
foo(42, use_my_impl);

它打印

Using my impl: 41
Using default impl: 43
Using default impl: 43

尽管传递了 use_my_impl_t 的实例,为什么最后一条语句没有调用我的实现?

这里的问题是 T 被推断为什么。在 foo(42, use_my_impl_t{}); 中,use_my_impl_t{} 是一个右值,因此 T 被推断为 use_my_impl_t 并且参数变为 use_my_impl_t&&.

对于foo(42, use_my_impl);use_my_impl是一个左值。这意味着 T 被推断为 use_my_impl_t & 以保持左值。这意味着参数变为 use_my_impl_t& && 其中 collapsesuse_my_impl_t&.

这就是导致 std::is_same<T, use_my_impl_t>::value 不起作用的原因,因为 use_my_impl_t&use_my_impl_t 不同。要解决此问题,您可以使用 std::decay_t 删除类型的引用和 cv 限定。那会给你

template<
    typename T,
    typename std::enable_if<std::is_same<std::decay_t<T>, use_my_impl_t>::value>::type * = nullptr>
void foo(int i, T&& t)
{
    std::cout << "Using my impl: " << --i << std::endl;
}

template<
    typename T,
    typename std::enable_if<!std::is_same<std::decay_t<T>, use_my_impl_t>::value>::type * = nullptr>
    void foo(int i, T&& t)
{
    std::cout << "Using default impl: " << ++i << std::endl;
}

然后输出

Using my impl: 41
Using default impl: 43
Using my impl: 41

使用您的驱动程序代码。