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