构造函数中is_constructible的SFINAE编译问题排查
构造函数模板SFINAE条件错误导致编译失败的分析与解决
问题代码
#include <iostream> #include <vector> #include <type_traits> enum class Error { OK = 0, NOK = 1 }; template <typename T, typename E = Error> class Foo { public: template <typename U = T, std::enable_if_t<std::is_constructible<U>::value, bool> = true> Foo(U&& other) { new (&value_) T(std::forward<U>(other)); } private: union { T value_; E error_; }; }; // 使用代码:编译失败位置 Foo<std::int32_t> Check(std::vector<std::int32_t>& data) { return data.at(0); } int main() { std::vector<std::int32_t> data{1, 2}; auto res = Check(data); return 0; }
编译报错
could not convert ‘(& data)->std::vector::at(0)’ from ‘__gnu_cxx::__alloc_traits, int>::value_type’ {aka ‘int’} to ‘Foo’
问题根源
你对模板参数推导的理解有误:
data.at(0)返回的是左值引用(std::int32_t&),而构造函数的U&&是万能引用,根据推导规则,左值会被推导为左值引用类型,即U会被推导为std::int32_t&,而非你预想的std::int32_t。- 原条件
std::is_constructible<U>::value检查的是std::int32_t&是否可构造——但引用类型本身无法默认构造,这个条件的结果是false,导致构造函数模板被SFINAE机制禁用,编译器找不到合适的构造函数完成类型转换。
正确解决方案
将构造函数的SFINAE条件修改为检查T是否能由U构造:
template <typename U = T, std::enable_if_t<std::is_constructible<T, U>::value, bool> = true> Foo(U&& other) { new (&value_) T(std::forward<U>(other)); }
方案有效性说明
当U被推导为std::int32_t&时,std::is_constructible<T, U>::value等价于std::is_constructible<std::int32_t, std::int32_t&>::value,这个条件结果为true(int可以从int&构造),构造函数模板会被正常启用,编译器可以匹配到该构造函数完成转换。
同时这个条件也更贴合你的实际需求:你需要的是用U类型的参数构造成员T,而非检查U自身的可构造性。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

