考虑这个结构。它是一个范围,但使用模板而不是构造函数,使值静态:
template <int min_v, int max_v>
struct num_range final {
constexpr static int min = min_v;
constexpr static int max = max_v;
}
// now you can do
std::cout << num_range<1,3>::min << '\n'; // prints 1
但这被固定为
int
。如果我希望 C++ 从第一个参数推导数字类型并将其强制应用于第二个参数怎么办?
我试过这个:
template <typename TNumber>
template <TNumber min_v, TNumber max_v>
struct num_range final
我按照相反的顺序尝试了。我知道你可以做到这一点:
template <auto min_v, decltype(min_v) max_v>
struct num_range final {
using num_type = decltype(min_v);
constexpr static num_type min = min_v;
constexpr static num_type max = max_v;
};
但我认为 C++17 中不允许使用
auto
模板参数,这正是我的目标。它似乎也没有强制第二个模板参数的类型,因为这编译得很好:
std::cout << num_range<-10, 12U>::min << '\n'; // prints -10
如果您知道如何使用可变模板执行此操作,强制所有数字为同一类型,则会加分。
我认为 C++17 中不允许使用自动模板参数
C++17 中允许。
它似乎也没有强制第二个模板参数的类型
是的,传递模板参数时允许进行一些隐式转换。改为这样做:
template <auto min_v, auto max_v>
struct num_range
{
static_assert(std::is_same_v<decltype(min_v), decltype(max_v)>);
constexpr static auto min = min_v;
constexpr static auto max = max_v;
};
或者在 C++20 中:
template <auto min_v, auto max_v>
requires std::is_same_v<std::is_same_v<decltype(min_v), decltype(max_v)>
struct num_range
{
constexpr static auto min = min_v;
constexpr static auto max = max_v;
};
你原来的方法确实有效,只是你没有让它引人注目:
using range = num_range<static_cast<signed char>(0), 200>;
clang 拒绝这一点:
<source>:8:54: error: non-type template argument evaluates to 200, which cannot be narrowed to type 'decltype('\x00')' (aka 'signed char') [-Wc++11-narrowing]
8 | using range = num_range<static_cast<signed char>(0), 200>;
|
在您的示例中
num_range<-10, 12U>
,12U
可以转换为int
(-10
的类型),因此一切都可以正常编译。
诚然,这种行为令人惊讶,因为
-10
和 12U
仍然是不同的类型,并且 num_range<-10, 12U>
编译直观上是错误的,但隐式转换使其可以工作。如果你想防止这种情况,你需要分别推导这两种类型,然后检查它们是否相同(@HolyBlackCat为此提供了两种解决方案)。
但我认为 C++17 中不允许使用自动模板参数
是的,请参阅cppreference:模板参数。
如果您知道如何使用可变模板执行此操作,强制所有数字为同一类型,则会加分。
template <auto Head, decltype(Head)... Tail>
/* ... */
或者,类似于@HolyBlackCat的解决方案,使用
std::enable_if
:
template <auto Head, auto... Tail, std::enable_if_t<(std::is_same_v<decltype(Head), decltype(Tail)> && ...), int> = 0>
/* ... */