使用 std::enable_if 和 constexpr 参数进行重载解析不能按预期工作

问题描述

我正在尝试根据模板参数之一的类型为函数模板添加函数重载,并将定义为 consexpr 的参数传递到其他地方以帮助解决重载问题。

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

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

使用您的驱动程序代码。