Clang 和 GCC 下 对构造函数使用SFINAE 的问题 #18
-
关于对复制和移动构造函数应用SFINAE的问题起因最初的目的是在我所编写的类中控制移动和构造函数的使用,以确保在不满足特定条件时不调用定义的移动函数,而是调用复制构造函数。我通过简短的代码尝试复现了这个问题。需要注意的是,在MSVC编译器中,这个问题被忽略了,编译通过。 复现以下是相关的代码: #include <type_traits>
template <class T>
class A {
private:
T a{};
public:
A() {}
template <class En = std::enable_if_t<std::is_same_v<T, int>>>
A(const A& aa) {
a = aa.a;
}
template <class En = std::enable_if_t<std::is_same_v<T, int>>>
A(A&& aa) {
a = aa.a;
}
};
int main() {
A<int> b{}; // is_same_v<int, int>为true,正常
A<double> bb{}; // is_same_v<int, int>为false,出错
} 然而,Clang 编译器给出了以下错误信息: error: no type named 'type' in 'std::enable_if<false>'; 'enable_if' cannot be used to disable this declaration 问题因此,是否能够在复制或者构造函数中应用 SFINAE 机制呢? 链接 |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 10 replies
-
你这里构造函数都不是模板函数,你的 SFINAE 是对谁用? |
Beta Was this translation helpful? Give feedback.
-
注意实例化的时机,进行延迟实例化即可 |
Beta Was this translation helpful? Give feedback.
-
首先问题描述本身存在一些错误:复制构造函数与移动构造函数都不能是函数模板。 如果要对复制或移动特殊成员函数使用 SFINAE,可以这么做:
我们可以记住一条规则:参与传统 SFINAE 的模板形参一定要是在当前模板中引入的。 不过,如果题主的意图不是“使用复制或移动构造函数”,而是“使用函数模板,但使之能用于复制或移动构造”的话,可以这么做。(Godbolt link) #include <type_traits>
#include <utility>
template<class T>
class A{
private:
T a{};
A(const volatile A&) = delete; // This is a copy ctor!
public:
A() = default;
template<class U = T, class = std::enable_if_t<std::is_same_v<U, int>>>
A(const A & aa) : a(aa.a) {}
template<class U = T, class = std::enable_if_t<std::is_same_v<U, int>>>
A(A && aa) noexcept : a(std::move(aa.a)) {}
}; 此处我们显式声明了一个复制构造函数,它会抑制复制构造函数与移动构造函数的隐式声明。从而下面两个函数模板会得到选择,并被用于复制或移动构造。 |
Beta Was this translation helpful? Give feedback.
首先问题描述本身存在一些错误:复制构造函数与移动构造函数都不能是函数模板。
所以样例代码中涉及的构造函数都不是复制或移动构造函数。而且复制构造函数和移动构造函数仍然会被隐式声明(假设模板头被改正),从而题主引入的函数模板不会得到选择。
如果要对复制或移动特殊成员函数使用 SFINAE,可以这么做:
requires
,或= delete;
或= default;
的基类或非静态数据成员,标准库中std::optional
与std::variant
的实现就使用各种专门定制的基类(因为需要用 C++17 语核实现)。我们可以记住一条规则:参与传统 SFINAE 的模板形参一定要是在当前模板中引入的。
外围的模板形参不再参与推导或替换,从而会导致硬错误。
不过,如果题主的意图不是“使用复制或移动构造函数”,而是“使用函数模板,但使之能用于复制或移动构造”的话,可以这么做。(Godbolt link)