考虑以下课程并具体注意
Concrete<T>::copy_assign
:
struct TypeErased
{
TypeErased() = default;
TypeErased& operator=(TypeErased const& other) {
copy_assign(other);
return *this;
}
virtual void copy_assign(TypeErased const& other) = 0;
virtual ~TypeErased(){}
};
template <typename T>
class Concrete : public TypeErased
{
public:
template <typename... Args>
Concrete(Args&&... args) : value(std::forward<Args>(args)...) {}
T const& get_value() const {
return value;
}
void copy_assign(TypeErased const& other) override final
{
copy_assign_impl(other);
}
private:
template <typename U=T>
void copy_assign_impl(TypeErased const& other) {
if constexpr (std::is_copy_assignable_v<U>) {
value = static_cast<Concrete<U> const&>(other).get_value();
} else {
throw std::logic_error("Nope!");
}
}
T value;
};
我需要 constexpr-if 才能实例化
Concrete<T>
实例,即使 T
不可复制分配。如果尝试复制分配一个不可复制分配的 Concrete<T>
,我想抛出一个运行时错误。T
现在我的问题是,if-constexpr 条件并不详尽。我不断遇到这样的情况:条件
struct NoCopy {
NoCopy() = default;
NoCopy(NoCopy const&) = delete;
NoCopy& operator=(NoCopy const&) = delete;
};
int main()
{
// These work:
Concrete<double> x1; // trivial
Concrete<std::vector<double>> x2;
Concrete<NoCopy> x3;
Concrete<std::unique_ptr<double>> x4;
Concrete<std::optional<NoCopy>> x5;
}
的计算结果为
std::is_copy_assignable_v<T>
,但表达式 true
无效。例如,以下所有实例化都是编译器错误:value = static_cast<Concrete<T> const&>(other).get_value();
https://godbolt.org/z/6dxh1a7rK是否有我可以使用的类型特征或某些 SFINAE 魔法,仅使用 C++17 功能来可靠地测试表达式 Concrete<boost::optional<NoCopy>> x6;
Concrete<std::vector<NoCopy>> x7;
Concrete<std::vector<std::unique_ptr<double>>> x8;
Concrete<boost::variant<std::string&, boost::optional<std::string>&>> // I know this is weird, but the types used in Concrete come from external libraries and are not in my hand
的有效性?
value = static_cast<Concrete<T> const&>(other).get_value();
:
std::is_copy_assignable 和 boost::Optional 的意外行为
“SFINAE特殊会员或支持不完整类型:最多选择一个”永远永远boost::optional
的计算结果为 true,但表达式
无效。例如,以下所有实例化都是编译器错误:std::is_copy_assignable_v<T>
value = static_cast<Concrete<T> const&>(other).get_value();
这里的根本原因是
能够处理这样的邪恶类型:
Concrete<boost::optional<NoCopy>> x6;
Concrete<std::vector<NoCopy>> x7;
Concrete<std::vector<std::unique_ptr<double>>> x8;
此类型声称
是可复制分配的,但是一旦您尝试实例化其template<class T>
struct Evil {
Evil& operator=(const Evil&) {
static_assert(sizeof(T) == 0);
}
};
static_assert(std::is_copy_assignable_v<Evil<int>>);
的定义,它就会出现硬错误。 (这个错误“不是在直接上下文中”——它不仅仅是替换失败。)
operator=
表现得像
vector<NoCopy>
?”答案很简单“SFINAE特殊会员或支持不完整类型:最多选择一个”。
Evil<int>
必须预先决定是否要查看并查看
vector
是否可复制(在这种情况下,NoCopy
必须是完整类型,然后你就不能再拥有NoCopy
了),或者是否会乐观地假设它是可复制的(它确实如此;然后你就得到了
struct Node { vector<Node> children; }
的情况)。