从未选择用于实例化的默认概念约束函数
Defaulted concept-constrained function never selected for instantiation
在使用 C++20 时,我遇到了一个奇怪的问题,我不确定这是由于 C++20 的婴儿期导致的编译器缺陷,还是这是否是正确的行为。
问题是未正确选择特定的约束函数,或者产生编译错误 -- 完全取决于定义的顺序。
在特定情况下会出现这种情况:
- 构造函数/析构函数/成员函数受
requires
和 约束
- 此构造函数/析构函数/成员函数被隐式删除
T
的某些实例化 不满足 requires
子句
例如,考虑这个基本的 naive_optional<T>
实现:
template <typename T>
class naive_optional {
public:
naive_optional() : m_empty{}, m_has_value{false}{}
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
~naive_optional() {
if (m_has_value) {
m_value.~T();
}
}
private:
struct empty {};
union {
T m_value;
empty m_empty;
};
bool m_has_value;
};
此代码适用于 naive_optional<int>
等普通类型,但由于隐式删除的析构函数而无法实例化 naive_optional<std::string>
等非普通类型:
<source>:26:14: error: attempt to use a deleted function
auto v = naive_optional<std::string>{};
^
<source>:8:5: note: explicitly defaulted function was implicitly deleted here
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
^
<source>:17:11: note: destructor of 'naive_optional<std::basic_string<char>>' is implicitly deleted because variant field 'm_value' has a non-trivial destructor
T m_value;
^
1 error generated.
如果更改定义的顺序,使受约束的函数低于不受约束的函数,则这可以正常工作——但每次都会选择不受约束的函数:
...
~naive_optional() { ... }
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
...
我的问题最终是:这种行为 正确吗 -- 我是否在滥用 requires
?如果这是正确的行为,是否有任何简单的方法可以有条件地完成 default
-ing 本身不是模板的构造函数/析构函数/函数?我一直在寻求避免继承基本 class 实现的“传统”方法。
您的代码是正确的,确实是支持此类事情的激励示例(参见 P0848, of which I am one of the authors). Clang simply doesn't implement this feature yet (as you can see here)。
gcc 和 msvc accept your code.
,您不再需要 empty
,在 C++20 中只需 union { T val; }
就足够了
在使用 C++20 时,我遇到了一个奇怪的问题,我不确定这是由于 C++20 的婴儿期导致的编译器缺陷,还是这是否是正确的行为。
问题是未正确选择特定的约束函数,或者产生编译错误 -- 完全取决于定义的顺序。
在特定情况下会出现这种情况:
- 构造函数/析构函数/成员函数受
requires
和 约束
- 此构造函数/析构函数/成员函数被隐式删除
T
的某些实例化 不满足requires
子句
例如,考虑这个基本的 naive_optional<T>
实现:
template <typename T>
class naive_optional {
public:
naive_optional() : m_empty{}, m_has_value{false}{}
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
~naive_optional() {
if (m_has_value) {
m_value.~T();
}
}
private:
struct empty {};
union {
T m_value;
empty m_empty;
};
bool m_has_value;
};
此代码适用于 naive_optional<int>
等普通类型,但由于隐式删除的析构函数而无法实例化 naive_optional<std::string>
等非普通类型:
<source>:26:14: error: attempt to use a deleted function
auto v = naive_optional<std::string>{};
^
<source>:8:5: note: explicitly defaulted function was implicitly deleted here
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
^
<source>:17:11: note: destructor of 'naive_optional<std::basic_string<char>>' is implicitly deleted because variant field 'm_value' has a non-trivial destructor
T m_value;
^
1 error generated.
如果更改定义的顺序,使受约束的函数低于不受约束的函数,则这可以正常工作——但每次都会选择不受约束的函数:
...
~naive_optional() { ... }
~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
...
我的问题最终是:这种行为 正确吗 -- 我是否在滥用 requires
?如果这是正确的行为,是否有任何简单的方法可以有条件地完成 default
-ing 本身不是模板的构造函数/析构函数/函数?我一直在寻求避免继承基本 class 实现的“传统”方法。
您的代码是正确的,确实是支持此类事情的激励示例(参见 P0848, of which I am one of the authors). Clang simply doesn't implement this feature yet (as you can see here)。
gcc 和 msvc accept your code.
,您不再需要
empty
,在 C++20 中只需 union { T val; }
就足够了