랩핑 된 객체가 생성, 액세스, 수정 및 소멸 될 때 계측 할 수있는 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
각 템플릿 호출 사이트에서 래핑 된 변수를 수행하여 수동으로이 문제를 해결할 수 있지만 (첫 번째 호출에서 볼 수 있음) 대규모 코드 기반으로 작업하고 있기 때문에 확장이 잘되지 않습니다. 유사한 질문 은 각 템플릿 기능을 함수로 인라인하는 것을 제안 friend
하지만,이를 위해서는 각 템플릿을 식별하고 복사해야하며 확장되지 않습니다.
(1) 템플릿 함수로이 변환 문제를 해결하거나 (2)이 문제없이 소스 수준에서 변수를 계측하는 방법에 대한 조언을 주시면 감사하겠습니다.
이 예의 결함은 is_same
. 동일한 유형의 두 인수가 필요하다고 선언합니다. 이는 필요하지 않은 요구 사항이며 해당 유형이 필요로하는를 갖도록 요구 ==
하지 않습니다.
물론 그렇지 않으면 어렵고 장황하기 때문에 템플릿 함수를 제대로 제한하지 않는 C ++를 찾는 것이 일반적입니다. 저자는 실용적인 지름길을 택합니다. 즉, 인터페이스를 수정하는 접근 방식이 is_same
아닙니까?
// 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)) {}
이것들은 명시 적이 지 않습니다. A T
는 암시 적으로 a로 변환 할 수 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; }
이것들은 ref-qualified가 아니므로 래퍼가 rvalue 인 경우에도 lvalue 참조를 반환합니다. 그것은 잘못된 것 같습니다.
마지막으로 구성 및 변환은 정확한 유형 만 고려합니다 T
. 그러나 모든 장소 T
가 사용되며 다른 유형에서 암시 적으로 변환 될 수 있습니다. 그리고 두 개의 변환은 C ++에서 허용되지 않습니다. 따라서 래퍼 유형의 경우 결정을 내릴 수 있으며 이는 종종 a T
가 구성 가능한 모든 것에서 구성을 허용한다는 것을 의미 합니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다