仅包含可转换为 T 的类型的参数包

问题描述 投票:0回答:1

考虑以下简单的向量类:

#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
也不会产生编译器错误。

这是代码。

c++ c++20 variadic-templates variadic-functions c++-concepts
1个回答
0
投票

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>

© www.soinside.com 2019 - 2024. All rights reserved.