这是标准中的一个缺陷(我一开始没有发现它,因为它是为tuple
).
https://wg21.link/lwg2051 https://wg21.link/lwg2051
进一步讨论和提议的决议(2015 年 5 月在 Lenexa 上投票通过 C++1z):
https://wg21.link/n4387 https://wg21.link/n4387
根本问题是转换构造函数pair
and tuple
检查is_convertible
这需要一个可访问的复制/移动构造函数。
详细:转换构造函数模板std::pair<T1, T2>
and std::tuple
看起来像这样:
template<class U, class V>
constexpr pair(U&&, V&&);
但这太贪婪了:当您尝试将其与不兼容的类型一起使用时,它会产生硬错误,并且std::is_constructible<pair<T1, T2>, U, V>::value
一直会true
因为这个构造函数模板的声明可以被实例化any types U
and V
。因此,我们需要限制这个构造函数模板:
template<class U, class V,
enable_if_t<check_that_we_can_construct_from<U, V>::value>
>
constexpr pair(U&& u, V&& v)
: t1( forward<U>(u) ), t2( forward<V>(v) )
{}
请注意,tx( forward<A>(a) )
可以打电话explicit
构造函数。因为这个构造函数模板pair
is 未标记为显式,我们必须将其限制为not履行内部显式转换同时初始化其数据成员。因此,我们使用is_convertible
:
template<class U, class V,
std::enable_if_t<std::is_convertible<U&&, T1>::value &&
std::is_convertible<V&&, T2>::value>
>
constexpr pair(U&& u, V&& v)
: t1( forward<U>(u) ), t2( forward<V>(v) )
{}
在OP的情况下,没有隐式转换:类型是不可复制的,这会呈现定义的测试隐式可转换性格式错误:
// v is any expression of type `int`
foobar f = v; // definition of implicit convertibility
根据标准的这种复制初始化形式会在右侧生成一个临时值,并用以下命令进行初始化v
:
foobar f = foobar(v);
其中右侧应被理解为隐式转换(因此没有explicit
可以调用构造函数)。但是,这需要将右侧的临时文件复制或移动到f
(直到 C++1z,请参阅p0135r0 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html).
总结:int
不能隐式转换为foobar
因为隐式可转换性的定义方式需要可移动性,因为 RVO 不是强制性的。pair<int, foobar>
不能从构造{1, 2}
因为这pair
构造函数模板不是explicit
因此需要隐式转换。
更好的解决方案explicit
VS 隐式转换问题,如改进pair and tuple http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4387.html就是拥有explicit
magic:
构造函数是explicit
当且仅当is_convertible<U&&,
first_type>::value
is false
or is_convertible<V&&, second_type>::value
is false
.
通过此更改,我们可以放宽隐式可转换性的限制(is_convertible
) 到“显式可转换性”(is_constructible
)。实际上,在这种情况下我们得到了以下构造函数模板:
template<class U, class V,
std::enable_if_t<std::is_constructible<U&&, int>::value &&
std::is_constructible<V&&, foobar>::value>
>
explicit constexpr pair(U&&, V&&);
这是不受限制的足以使std::pair<int, foobar> p{1, 2};
valid.