将 std::enable_if 与匿名类型参数一起使用

Using std::enable_if with anonymous type parameters

我尝试将 std::enable_if 与未使用且未命名的类型参数一起使用,以免扭曲 return 类型。但是,以下代码无法编译。

#include <iostream>

template <typename T, typename = std::enable_if_t<!std::is_integral<T>::value>>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
  foo<float>();
  foo<int>();
}

编译器说:

7:3: error: redefinition of 'template<class T, class> T foo()'
4:3: note: 'template<class T, class> T foo()' previously declared here
 In function 'int main()':
11:12: error: no matching function for call to 'foo()'
11:12: note: candidate is:
4:3: note: template<class T, class> T foo()
4:3: note: template argument deduction/substitution failed:

这里有什么问题?我必须如何更改代码才能使其编译?教科书 "Discovering Modern C++" 明确鼓励使用带有匿名类型参数的 std::enable_if

编辑:我知道如果我将 std::enable_if 放入 return 类型,它会起作用。但是,我的目的是获取更多详细信息,了解为什么如果我将它与匿名类型参数一起使用它不起作用。正如我所说,我的教科书鼓励使用匿名类型参数的变体,所以我想知道为什么我的代码无法编译。

您可以通过多种方式实现 SFINAE 离开功能。你通常应该避免添加额外的 function/template 参数,而只是与 return 类型混在一起。

template <typename T>
auto foo() -> std::enable_if_t<!std::is_integral<T>::value, T>
{ std::cout << "non-integral" << std::endl; return T(); }

template <typename T>
auto foo() -> std::enable_if_t<std::is_integral<T>::value, T>
{ std::cout << "integral" << std::endl; return T(); }

您可以将 enable_if 放入 return 类型:

template <typename T>
std::enable_if_t<!std::is_integral<T>::value,T>
foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
foo() { std::cout << "integral" << std::endl; return T(); }

顺便说一句,enable_if_t 可从 C++14 获得,因此您可能想改为说 typename std::enable_if<std::is_integral<T>::value, T>::type。满嘴的。

但是更惯用(和可读)的是基于类型进行调度:

template <typename T>
T foo_impl(std::false_type) { std::cout << "non-integral" << std::endl; return T(); }

template <typename T>
T foo_impl(std::true_type) { std::cout << "integral" << std::endl; return T(); }

template <typename T>
T foo(){
    return foo_impl<T>(typename std::is_integral<T>::type{});
}

你的错误是你在等号右边使用了enable_if_t

你必须在左边使用它

#include <iostream>
#include <type_traits>

template <typename T, std::enable_if_t<!std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
  foo<float>();
  foo<int>();
}

但这适用于 C++14。

在 C++11 中(您的问题被标记为 C++11)您没有 enable_if_t

代码变为

#include <iostream>
#include <type_traits>

template <typename T,
          typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T,
          typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
  foo<float>();
  foo<int>();
}

However, my intention is to get some more details why it does not work if I use it with anonymous type parameters.

默认值不参与重载决策,因此您实际上是在重新定义相同的函数。

让我们简化您的示例:

template<typename = int>
void f() {}

template<typename = void>
void f() {}

int main() {
    f<>();
}

上面的代码无法编译,因为它不知道你想调用哪个版本的f

在你的情况下,如果我将 foo 调用为 foo<void, void>,我会遇到几乎相同的问题。
编译器无法猜出我的意图,第二个参数有默认值并不意味着你不能传入不同的类型。

因此,代码格式不正确,编译器正确地给了你一个错误。


附带说明一下,您仍然可以在不使用 return 类型中的 std::enable_if_t 的情况下使用它。
例如:

#include <type_traits>
#include <iostream>

template <typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
    foo<float>();
    foo<int>();
}

当我试图弄清楚 OP 的(错误)假设是什么并解释为什么会这样时,@T.C。在对此答案的评论中正确指出了对实际原因的关注。
值得引用他的评论来为答案添加更多细节:

It's not overload resolution; it's declaration matching. There are no two overloads in the first place for any ambiguity to arise. It's two redefinition errors: the function template, and the default template argument.