当一个条件分支是从基础 class 继承时,为什么这个 SFINAE 不能与 enable_if 一起工作?
Why does this SFINAE not work with enable_if when one conditional branch is inherited from the base class?
#include <bits/stdc++.h>
#include <type_traits>
// Type your code here, or load an example.
template <typename Types>
class C1 {
public:
using A=typename Types::A;
using B=typename Types::B;
template <typename Dummy = void>
inline typename std::enable_if<std::is_same<A, B>::value, Dummy>::type f() { }
};
template <typename Types>
class C2 : public C1<Types> {
public:
using A=typename Types::A;
using B=typename Types::B;
template <typename Dummy = void>
inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
};
template <typename Types>
class C3 : public C2<Types> {
public:
using A=typename Types::A;
using B=typename Types::B;
};
struct Types{
using A = int;
using B = int;
};
int main() {
C3<Types> c;
c.f();
return 0;
}
当我尝试在 A 和 B 不同的情况下编译以上代码时,出现以下错误:
<source>: In function 'int main()':
<source>:42:9: error: no matching function for call to 'C3<Types>::f()'
42 | c.f();
| ^
<source>:23:77: note: candidate: 'template<class Dummy> typename std::enable_if<(! std::is_same<typename Types::A, typename Types::B>::value), Dummy>::type C2<Types>::f() [with Dummy = Dummy; Types = Types]'
23 | inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
| ^
<source>:23:77: note: template argument deduction/substitution failed:
<source>: In substitution of 'template<class Dummy> typename std::enable_if<false, Dummy>::type C2<Types>::f<Dummy>() [with Dummy = void]':
<source>:42:9: required from here
<source>:23:77: error: no type named 'type' in 'struct std::enable_if<false, void>'
请注意,我提供的代码并不是我使用的确切代码,而是一个最小的可重现示例
编辑:提出一个最小的可重现示例,使用 godbolt 代替早期的示例,以便更好地理解情况
与此类问题一样,它归结为 SFINAE 的定义。 S 代表“替换”,它发生在 到 我们试图实例化的模板中。该模板是成员 f
,而不是 C
。
尽管 C
也是模板,并且 A
和 B
都是 C
中的依赖类型,但它们 不是 依赖类型,当 f
被实例化时。他们已经为人所知。因此,条件 std::is_same<A, B>::value
的值不依赖于 f
的任何模板参数。它不依赖变电站进入 f
。这触发了 C++11 标准中的以下条款(摘自发布前的最后一份草案):
[temp.res] (emphasis mine)
8 Knowing which names are type names allows the syntax of every
template definition to be checked. No diagnostic shall be issued for a
template definition for which a valid specialization can be generated.
If no valid specialization can be generated for a template definition, and that template is not instantiated, the template
definition is ill-formed, no diagnostic required.
这意味着无论 Types
是什么,如果它不支持 f
的条件,那么 f
的定义(甚至没有被实例化)已经是每当实例化 C
时格式错误。通常不需要对此进行诊断(因为在一般情况下检查它很棘手),但编译器可以经常及早诊断它,并会告诉您问题所在。
现在,至于如何修复它,只需使f
值的条件依赖于它自己的模板参数即可。简单重写即可
template <bool Dummy = std::is_same<A, B>::value>
inline auto f(vector<int>& ctx, const string& r) ->
typename std::enable_if<Dummy>::type { }
现在条件取决于正确上下文中的变电站。
当然,即使您修复了SFIANE 问题,您仍然需要确保重载集由正确的成员组成。 C2
中的f
隐藏了C1
中的f
。向 C2
添加一个 using 声明,因此它仍然是一个候选
using C1<Types>::f;
#include <bits/stdc++.h>
#include <type_traits>
// Type your code here, or load an example.
template <typename Types>
class C1 {
public:
using A=typename Types::A;
using B=typename Types::B;
template <typename Dummy = void>
inline typename std::enable_if<std::is_same<A, B>::value, Dummy>::type f() { }
};
template <typename Types>
class C2 : public C1<Types> {
public:
using A=typename Types::A;
using B=typename Types::B;
template <typename Dummy = void>
inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
};
template <typename Types>
class C3 : public C2<Types> {
public:
using A=typename Types::A;
using B=typename Types::B;
};
struct Types{
using A = int;
using B = int;
};
int main() {
C3<Types> c;
c.f();
return 0;
}
当我尝试在 A 和 B 不同的情况下编译以上代码时,出现以下错误:
<source>: In function 'int main()':
<source>:42:9: error: no matching function for call to 'C3<Types>::f()'
42 | c.f();
| ^
<source>:23:77: note: candidate: 'template<class Dummy> typename std::enable_if<(! std::is_same<typename Types::A, typename Types::B>::value), Dummy>::type C2<Types>::f() [with Dummy = Dummy; Types = Types]'
23 | inline typename std::enable_if<!std::is_same<A, B>::value, Dummy>::type f() { }
| ^
<source>:23:77: note: template argument deduction/substitution failed:
<source>: In substitution of 'template<class Dummy> typename std::enable_if<false, Dummy>::type C2<Types>::f<Dummy>() [with Dummy = void]':
<source>:42:9: required from here
<source>:23:77: error: no type named 'type' in 'struct std::enable_if<false, void>'
请注意,我提供的代码并不是我使用的确切代码,而是一个最小的可重现示例
编辑:提出一个最小的可重现示例,使用 godbolt 代替早期的示例,以便更好地理解情况
与此类问题一样,它归结为 SFINAE 的定义。 S 代表“替换”,它发生在 到 我们试图实例化的模板中。该模板是成员 f
,而不是 C
。
尽管 C
也是模板,并且 A
和 B
都是 C
中的依赖类型,但它们 不是 依赖类型,当 f
被实例化时。他们已经为人所知。因此,条件 std::is_same<A, B>::value
的值不依赖于 f
的任何模板参数。它不依赖变电站进入 f
。这触发了 C++11 标准中的以下条款(摘自发布前的最后一份草案):
[temp.res] (emphasis mine)
8 Knowing which names are type names allows the syntax of every template definition to be checked. No diagnostic shall be issued for a template definition for which a valid specialization can be generated. If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required.
这意味着无论 Types
是什么,如果它不支持 f
的条件,那么 f
的定义(甚至没有被实例化)已经是每当实例化 C
时格式错误。通常不需要对此进行诊断(因为在一般情况下检查它很棘手),但编译器可以经常及早诊断它,并会告诉您问题所在。
现在,至于如何修复它,只需使f
值的条件依赖于它自己的模板参数即可。简单重写即可
template <bool Dummy = std::is_same<A, B>::value>
inline auto f(vector<int>& ctx, const string& r) ->
typename std::enable_if<Dummy>::type { }
现在条件取决于正确上下文中的变电站。
当然,即使您修复了SFIANE 问题,您仍然需要确保重载集由正确的成员组成。 C2
中的f
隐藏了C1
中的f
。向 C2
添加一个 using 声明,因此它仍然是一个候选
using C1<Types>::f;