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

时间:2016-10-25 21:53:33

标签: c++ c++11 templates sfinae enable-if

我尝试将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:

这是什么问题?我如何更改代码以使其编译?教科书&#34;发现现代C ++&#34;明确鼓励使用std::enable_if匿名类型参数。

编辑:我知道如果我将std::enable_if放入返回类型中它会起作用。但是,我的目的是获得更多细节,如果我将它与匿名类型参数一起使用它不起作用。正如我所说,我的教科书鼓励使用匿名类型参数的变体,所以我想知道为什么我的代码不能编译。

4 个答案:

答案 0 :(得分:6)

  

但是,我的目的是获得更多详细信息,如果我将它与匿名类型参数一起使用,它为什么不起作用。

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

让我们简化你的例子:

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

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

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

上面的代码无法编译,因为它无法知道您要调用的f版本。

在您的情况下,如果我将foo调用为foo<void, void>,我几乎会遇到同样的问题。
编译器无法猜测我的意图是什么以及第二个参数具有默认值的事实并不意味着您无法传递不同的类型。

因此,代码格式错误,编译器正确地给出了错误。

作为旁注,您可以在不使用返回类型中的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。正确地将注意力集中在对这个答案的评论中的实际原因上 值得引用他的评论以在答案中添加更多细节:

  

它不是重载决议;它的声明匹配。首先出现任何歧义都没有两个重载。它有两个重定义错误:函数模板和默认模板参数。

答案 1 :(得分:3)

您可以将enable_if放入返回类型:

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{});
}

答案 2 :(得分:2)

有几种方法可以让SFINAE离开功能。您通常应该避免添加额外的函数/模板参数,只需与返回类型混合。

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(); }

答案 3 :(得分:2)

您的错误是您在等号右侧使用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>();
}