- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在为 C++ 类型编写一个包装器类,它允许我在构建、访问、修改和销毁包装对象时进行检测。为了使这对原始代码透明,我将隐式转换函数包含回基础类型,但是当包装对象直接传递给模板时这会失败,因为不评估隐式转换。下面是一些演示此问题的代码:
#include <utility>
// simplified wrapper class
template <typename T>
class wrap {
T t;
public:
wrap() : t() {}
wrap(const T& _t) : t(_t) {}
wrap(T&& _t) : t(std::move(_t)) {}
constexpr operator T&() { return t; }
constexpr operator const T&() const { return t; }
};
// an example templated function
template <typename T>
bool is_same(const T& t1, const T& t2) { return t1 == t2;}
// second invocation fails due to template substitution failure
bool problem() {
wrap<int> w(5);
return is_same(static_cast<int>(w), 5) && is_same<>(w, 5);
}
我可以通过在每个模板调用站点对包装变量执行 static_cast
手动解决此问题(如第一次调用所示),但这不能很好地扩展,因为我正在使用庞大的代码库。 Similar questions建议将每个模板函数内联为一个 friend
函数,但这也需要识别和复制每个模板,这不会缩放。
对于如何 (1) 使用模板函数解决此转换问题,或 (2) 以其他方式在源代码级别检测变量而不会出现此问题,我将不胜感激。
最佳答案
这个例子的错误在于is_same
.它声明它需要两个相同类型的参数,这是它不需要的要求,并且不要求该类型具有 ==
。 ,它确实需要。
诚然,C++ 对模板函数的约束很差是很常见的,因为否则这样做既困难又冗长。作者采取了一条实用的捷径。也就是说,这不是修复 is_same
接口(interface)的方法吗? ?
// C++17 version. Close to std::equal_to<>::operator().
template <typename T, typename U>
constexpr auto is_same(T&& t, U&& u)
noexcept(noexcept(std::forward<T>(t) == std::forward<U>(u)))
-> decltype(std::forward<T>(t) == std::forward<U>(u))
{
return std::forward<T>(t) == std::forward<U>(u);
}
修正了is_same
,代码正常工作。
人们可以想象还有其他示例可能需要两个参数具有相同的类型。例如,如果返回类型取决于参数类型并且返回值可以来自以下任一:
template <typename T>
T& choose(bool choose_left, T& left, T& right) {
return choose_left ? left : right;
}
这种情况要少得多。但它实际上可能需要考虑来决定是使用底层类型还是包装类型。如果您在包装器类型中具有这种增强行为,并且有条件地使用包装值或底层值,是否应该包装底层值以继续获得增强行为,或者我们是否放弃增强?即使你能让它默默地选择这两种行为中的一种,你愿意吗?
但是,您仍然可以使获取值比说 static_cast<T>(...)
更容易。 ,例如通过提供访问器:
// given wrap<int> w and int i
is_same(w.value(), 5);
choose_left(true, w.value(), i);
我还有一些重要的意见:
wrap() : t() {}
这要求 T 是默认可构造的。 = default
做正确的事。
wrap(const T& _t) : t(_t) {}
wrap(T&& _t) : t(std::move(_t)) {}
这些不是明确的。 T
可以隐式转换为 wrap<T>
反之亦然。这在 C++ 中效果不佳。例如,true ? w : i
格式不正确。这导致 std::equality_comparable_with<int, wrap<int>>
为假,这将是 is_same
的合理要求.包装类型可能应该显式构造,并且可以隐式转换为基础类型。
constexpr operator T&() { return t; }
constexpr operator const T&() const { return t; }
这些不是引用限定的,因此即使包装器是右值,它们也会返回左值引用。这似乎是不明智的。
最后,构造和转换只考虑精确类型T
.但是任何地方T
被使用时,它可能从其他类型隐式转换而来。在 C++ 中不允许进行两次转换。因此,对于包装器类型,需要做出决定,这通常意味着允许从 T
的任何内容进行构造。可从.
关于c++ - 在模板评估期间允许隐式类型转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66181709/
我是一名优秀的程序员,十分优秀!