bol*_*lov 5 c++ templates overloading sfinae language-lawyer
SFINAE 技术是使用默认类来启用/禁用功能。但是,这不适用于重载函数,导致“模板参数重新定义默认参数”:
template <class T, class = std::enable_if_t<std::is_integral_v<T>>>
auto foo(T) { return 1; }
template <class T, class = std::enable_if_t<std::is_floating_point_v<T>>>
auto foo(T) { return 2; }
// error template parameter redefines default argument"
Run Code Online (Sandbox Code Playgroud)
常见的解决方案是使用默认的非类型模板参数:
template <class T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
auto foo(T) { return 1; }
template <class T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
auto foo(T) { return 2; }
// works
Run Code Online (Sandbox Code Playgroud)
这仅在条件“不同”时才有效。但是理解什么时候条件“不同”并不是那么简单。
从最明显的例子开始(相同(token by token)条件):
template <class T, std::enable_if_t<std::is_integral_v<T>, T> = 0>
auto foo(T) { return 1; }
template <class T, std::enable_if_t<std::is_integral_v<T>, T> = 0>
auto foo(T) { return 2; }
// error template parameter redefines default argument"
Run Code Online (Sandbox Code Playgroud)
使用两个不同的非依赖条件,评估为相同的值:
constexpr bool true_1 = true;
constexpr bool true_2 = true;
template <class T, std::enable_if_t<true_1, T> = 0>
auto foo(T) { return 1; }
template <class T, std::enable_if_t<true_2, T> = 0>
auto foo(T) { return 2; }
// error template parameter redefines default argument"
Run Code Online (Sandbox Code Playgroud)
使用两个不同的依赖条件,评估为相同的值:
template <class T> constexpr bool true_1 = true;
template <class T> constexpr bool true_2 = true;
template <class T, std::enable_if_t<true_1<T>, T> enable = 0>
auto foo(T) { return 1; }
template <class T, std::enable_if_t<true_2<T>, T> enable = 0>
auto foo(T) { return 2; }
// works
// (of course will give ambiguous call error when trying to call it,
// but the point here is you are allowed to declare them like this)
Run Code Online (Sandbox Code Playgroud)
在最后一个示例中,如果我调用foo(即foo(24))两个重载具有完全相同的参数和模板参数:
错误:对“foo”的调用不明确
Run Code Online (Sandbox Code Playgroud)return foo(12); ^~~注意:候选函数[with T = int, enable = 0]
Run Code Online (Sandbox Code Playgroud)auto foo(T) { return 1; } ^注意:候选函数[with T = int, enable = 0]
Run Code Online (Sandbox Code Playgroud)auto foo(T) { return 2; } ^
这似乎有效地实例化了两个相同的重载(根据声明,而不是定义)。
我所有的问题都非常密切相关,所以我在这里问他们所有人:
小智 1
首先:
\n// #1 Two templates with default type parameters.\ntemplate <typename T, typename T2 = int>\nvoid Foo(T) {}\ntemplate <typename T, typename T2 = char> // Error \xe2\x80\x93 redefinition.\nvoid Foo(T) {}\n\n// #2 Two templates with default non-type parameters.\ntemplate <typename T, int = 0>\nvoid Foo(T) {}\ntemplate <typename T, char = 'x'> // Allowed, but ambiguous for Foo<type>(value).\nvoid Foo(T) {}\nRun Code Online (Sandbox Code Playgroud)\n#1 和 #2 不相等。前两个模板参数 T2 类似(允许任何类型),只是默认值(类型)不同。
\n此外,标准规定:
\n\n\n可用的默认模板参数集是通过\n合并来自模板的所有先前声明的默认参数而获得的,\n其方式与默认函数参数 (11.3.6) 相同。\n示例:
\n
template<class T1, class T2 = int> class A;\ntemplate<class T1 = int, class T2> class A;\nRun Code Online (Sandbox Code Playgroud)\n\n\n相当于
\n
template<class T1 = int, class T2 = int> class A;\nRun Code Online (Sandbox Code Playgroud)\n同一位置的多个默认类型不能共存。
\n使用非类型参数,您实际上可以创建具有具体类型(类似于专业化)的单独模板,该模板从一开始就为这些函数创建单独的签名。
\nconstexpr bool true_1 = true;\nconstexpr bool true_2 = true;\n// Shorter version of second parameter, nameless.\ntemplate <class T, std::enable_if_t<true_1>* = 0> auto foo(T) { return 1; }\ntemplate <class T, std::enable_if_t<true_2>* = 0> auto foo(T) { return 2; }\nRun Code Online (Sandbox Code Playgroud)\n这不起作用,因为两个常量实际上总是相同的“true”,为非类型参数产生相同的类型。
\ntemplate <class T> constexpr bool true_1 = true;\ntemplate <class T> constexpr bool true_2 = true;\ntemplate <class T, std::enable_if_t<true_1<T>>* = 0> auto foo(T) { return 1; }\ntemplate <class T, std::enable_if_t<true_2<T>>* = 0> auto foo(T) { return 2; }\nRun Code Online (Sandbox Code Playgroud)\n该示例提供了不确定性因子 - T。当编译器只看到函数的声明时,它无法知道它将用什么来实例化它foo<>。可能有专业化true_2会指向错误。因此true_1和true_2被视为不同的类型。
| 归档时间: |
|
| 查看次数: |
138 次 |
| 最近记录: |