将 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& &&
其中 collapses 到 use_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
使用您的驱动程序代码。
我正在尝试根据其中一个模板参数的类型为函数模板添加函数重载,并在其他地方传递定义为 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& &&
其中 collapses 到 use_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
使用您的驱动程序代码。