我有一个表示特殊整数类型的模板类。
这个类的一个最小实现可能是这样的:
template<typename T>
struct Int {
static_assert(std::is_integral_v<T>, "Requires integral type.");
using NT = T;
T v;
explicit constexpr Int(T v) noexcept : v{v} {}
template<typename U, std::enable_if_t<std::is_integral_v<U>, bool> = true>
constexpr auto cast() const noexcept -> Int<U> {
return Int<U>{static_cast<U>(v)};
}
template<typename U, typename U::NT = 0>
constexpr auto cast() const noexcept -> Int<typename U::NT> {
return Int<typename U::NT>{static_cast<typename U::NT>(v)};
}
};
对于类的大多数常见用例,有许多预定义的类型名称:
using Int8 = Int<int8_t>;
using Int16 = Int<int16_t>;
using Int32 = Int<int32_t>;
using Int64 = Int<int64_t>;
目标是自然地使用这个类的类型,但要使用一组方法。其中一种方法是.cast<>()
方法,用于在以下整数类型之间进行转换:
int main(int argc, const char *argv[]) {
auto a = Int32{10};
auto b = a.cast<int64_t>();
auto c = a.cast<Int64>();
}
为了涵盖用户和模板中的广泛用途,强制转换模板参数应允许本机类型和以及模板类作为参数。指定int64_t
、Int64
或因此指定Int<int64_t>
将导致完全相同的结果。
我想将第二个cast
方法限制为Int
模板类的值
示例中显示的方法将适用于任何在名称空间中具有名为NT
的类型定义的类。在我的库中,NT
通常用于模板类,它对cast
方法的使用没有足够的限制。
下面的例子说明了我想避免的一种情况:
struct Unrelated {
using NT = int32_t;
};
int main(int argc, const char *argv[]) {
auto a = Int32{10};
auto b = a.cast<Unrelated>(); // NO! is confusing, shouldn't work.
}
是否有一种常用的方法来";启用";一个只包含自己类的模板实例的方法
- 我知道C++2x中有一些简单的解决方案。然而,我需要一个使用C++17的解决方案
- 接受所有积分类型的第一种铸造方法应保持完整
首先是Igor Tandetnik的一个类型特征(我自己的更丑(:
template<typename T> struct Int; // forward declaration
template <typename T> struct is_Int : std::false_type {};
template <typename T> struct is_Int<Int<T>> : std::true_type {};
template <typename T> inline constexpr bool is_Int_v = is_Int<T>::value;
然后您可以定义类及其cast
,如下所示:
template<typename T>
struct Int {
static_assert(std::is_integral_v<T>); // no SFINAE needed so use static_assert
using NT = T;
T v;
explicit constexpr Int(T v) noexcept : v{v} {}
template<class U> // accept all, no SFINAE needed
constexpr auto cast() const noexcept {
// use static_assert instead
static_assert(std::is_integral_v<U> || is_Int_v<U>); // using the trait
// ...and constexpr-if:
if constexpr (std::is_integral_v<U>) return Int<U>{static_cast<U>(v)};
else return U{static_cast<typename U::NT>(v)};
}
};