std::convertible_to概念误解与vector构造函数参数校验问题
C++ vector类构造函数的概念约束问题
你的代码实现
自定义vector类
#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; };
测试代码
int main() { vector<int, 3> x{ 1., 2, 3 }; // 你预期这里编译失败,但实际通过了 return 0; }
你的理解误区
- 对
std::convertible_to<T>的认知偏差:std::convertible_to<U, T>的核心要求是U可以隐式转换为T,而C++标准中允许double隐式转换为int(尽管会丢失小数部分,属于不安全转换,但语法上完全合法),因此double满足std::convertible_to<int>的约束,构造函数自然能通过编译。 - 混淆了转换的安全性与合法性:你认为
double转int只能显式转换,但实际上这是C++允许的隐式转换,只是编译器通常会给出警告(需开启对应警告等级,比如-Wconversion),并非语法错误。
修改方案
如果你希望禁止这种隐式转换,仅接受T类型本身,或者必须通过显式转换(如static_cast<T>)才能传入的类型,可以按以下方式处理:
方案1:仅接受与T完全相同的类型
使用std::same_as<T>约束参数类型,确保传入的参数类型和T完全一致:
#include <array> #include <concepts> template<typename T, std::size_t D> class vector { public: vector() = default; vector(std::same_as<T> auto const&... x) requires (sizeof...(x) == D) : m_x({ x... }) {} private: std::array<T, D> m_x; };
此时vector<int, 3> x{1., 2, 3};会直接编译失败,因为double和int不是相同类型。但这种方案会限制所有隐式转换,包括short转int这类安全的隐式转换。
方案2:允许显式转换但禁止隐式转换(C++23)
C++23新增了std::explicitly_convertible_to概念,专门用于描述“只能显式转换为目标类型”的场景。结合std::same_as,可以实现你的需求:
#include <array> #include <concepts> template<typename T, std::size_t D> class vector { public: vector() = default; vector(auto const&... x) requires (sizeof...(x) == D) && (... && (std::same_as<decltype(x), T> || std::explicitly_convertible_to<decltype(x), T>)) : m_x({ static_cast<T>(x)... }) {} private: std::array<T, D> m_x; };
此时直接传double会编译失败,必须显式转换为int才能通过:
// 合法:显式转换后传入 vector<int, 3> x{ static_cast<int>(1.), 2, 3 }; // 非法:直接传入double,触发编译错误 vector<int, 3> y{1., 2, 3};
方案3:自定义概念兼容C++20
如果使用C++20,没有std::explicitly_convertible_to,可以自己实现一个等价的概念:
#include <array> #include <concepts> template<typename U, typename T> concept explicitly_convertible_to = std::constructible_from<T, U> // U可以构造T(支持显式转换) && !std::convertible_to<U, T>; // U不能隐式转换为T template<typename T, std::size_t D> class vector { public: vector() = default; vector(auto const&... x) requires (sizeof...(x) == D) && (... && (std::same_as<decltype(x), T> || explicitly_convertible_to<decltype(x), T>)) : m_x({ static_cast<T>(x)... }) {} private: std::array<T, D> m_x; };
效果和C++23的方案一致,能禁止double隐式转换为int的场景。
内容的提问来源于stack exchange,提问作者0xbadf00d
相关产品推荐
相关产品推荐

