这有什么问题:
#include <type_traits>
struct A;
template<typename T>
struct B
{
template<typename=std::enable_if<std::is_copy_constructible<T>::value>>
void f1() {}
};
template<typename T>
struct C {};
// Type your code here, or load an example.
int main() {
// Following fails
B<A> b;
// Could use this:
// b.f1<C>();
// This complies
C<A> c;
return 0;
}
/* This to be in or not doesn't make a difference
struct A
{};
*/
Run Code Online (Sandbox Code Playgroud)
我在这里尝试过:https : //godbolt.org/z/NkL44s,使用了不同的编译器:
那么,为什么最近有更多的编译器拒绝这样做呢?实例化时B<A>,尚不清楚将以哪种形式f1使用或是否将全部使用。那么为什么编译器会抱怨呢?如果不是f1,如果真的用它成员模板函数只检查?
编辑:
如评论中所述,我在上面的代码中无意中犯了一个错误:std::enable_if应该是std::enable_if_t,如在此更正的操场上:https : //godbolt.org/z/cyuB3d
这改变了编译器无误地传递此代码的画面:
但是,问题仍然存在:为什么从未使用过的函数的默认模板参数会导致编译失败?
实例化类模板时B<A>,编译器实例化声明,但不实例化定义B<A>::f1:
类模板特化的隐式实例化导致
- 声明的隐式实例化,但非删除类成员函数、成员类、作用域成员枚举、静态数据成员、成员模板和友元的定义的隐式实例化;和
- [...]
类模板特化的隐式实例化不会导致类成员函数的默认参数或 noexcept 说明符的隐式实例化。
然而,这仅仅意味着函数体没有被实例化;剩下的就是。如果f1有默认参数,则不会实例化这些参数,但这std::enable_if是默认模板参数,因此此异常不适用。
以下内容被实例化:
Run Code Online (Sandbox Code Playgroud)template<typename = std::enable_if<std::is_copy_constructible<A>::value>> void f1() {}
默认模板参数格式错误,因为std::is_copy_constructible需要:
T应是完整类型、cvvoid或未知边界的数组。
在 C++20 中,使用尾随的 require 子句和std::copy_constructible概念:
void f1() requires std::copy_constructible<T> {}
Run Code Online (Sandbox Code Playgroud)
在 C++17 中:
template<typename U = T>
std::enable_if_t<std::is_copy_constructible_v<U>> f1() {}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为std::enable_if_t依赖于当前函数模板的模板参数,因此 SFINAE 可以按预期发生。
有关更多策略,请参阅std::enable_if 有条件编译成员函数。
| 归档时间: |
|
| 查看次数: |
98 次 |
| 最近记录: |