考虑以下简单的向量类:
#include <array>
#include <concepts>
template<typename T, std::size_t D>
class vector
{
public:
vector() = default;
vector(std::convertible_to<T> auto const&... x) requires (sizeof...(x) == D)
: m_x({ static_cast<T>(x)... })
{}
private:
std::array<T, D> m_x;
};
这个想法是,只有当所有
x
都是可转换的(或者要求它们是可构造会更好吗?)来键入T
时,该构造函数才应该适用。但是,以下代码编译时不会出现错误:
int main()
{
vector<int, 3> x{ 1., 2, 3 };
return 0;
}
那么,我在这里理解错了什么?第一个
x
是 1.
,其类型为 double
,不应该隐式转换为 int
。不过,它可以明确地转换为int
。也许这里是错误的。但是我应该如何更改代码呢? convertible_from
也不会产生编译器错误。
double
可以隐式转换为 int
,因此 std::convertible_to<double, int>
为 true。
您本质上是在要求“非狭窄地转换为”特征。在最基本的形式中,这可以通过以下方式完成:
template <typename From, typename To>
concept lossless_convertible_to = requires (From& f) {
To{f};
};
static_assert(!lossless_convertible_to<double, int>); // passes
static_assert(!lossless_convertible_to<int, double>); // passes
static_assert(lossless_convertible_to<float, double>); // passes
但是,
int -> double
被认为是缩小范围,这可能不是您想要的。另外,这不能正确处理 To
具有 std::initializer_list
构造函数的情况,因此这并不是真正的转换。
评论者 @Osyotr 链接了一个提案,它可以更正确地处理这个问题。
解决此问题的另一种方法是通过转化排名。从
int
到 double
的转化正在缩小,但由于 double
具有更高的转化等级,因此可能是可以接受的。我们也可以基于此创建一个概念:
template <typename From, typename To>
concept rank_increase_convertible_to = std::same_as<To, std::common_type_t<From, To>>;
static_assert(!rank_increase_convertible_to<double, int>); // passes
static_assert(rank_increase_convertible_to<int, double>); // passes
static_assert(rank_increase_convertible_to<float, double>); // passes
然后您可以在代码中将
std::convertible_to<T>
替换为 lossless_convertible_to<T>
或 rank_increase_convertible_to<T>
。