【问题标题】:Conversion from nullptr_t to type with implicit conversion from std::function使用 std::function 隐式转换从 nullptr_t 转换为类型
【发布时间】:2017-03-17 23:33:29
【问题描述】:

我遇到了以下几种编译器失败但在其他编译器中工作的情况。我想知道代码是否根据 C++11 标准有效。我们有一个将std::function<void()> 作为唯一参数构造函数的类型,并尝试创建一个包含它的std::pair,并将nullptr 作为参数传递给它在std::pair 中的位置:

#include <functional>
#include <utility>

struct Foo {
  Foo(std::function<void ()> f = nullptr) { }
};

typedef std::pair< void*, Foo > TestPair;

int main(void) {
  Foo f(nullptr); // always works
  //f = nullptr;  // never works
  TestPair p1(nullptr, static_cast< std::function<void()> >(nullptr)); // works
  TestPair p2(nullptr, nullptr); // fails in some compilers
  return 0;
}

您可以test it here 使用不同的编译器。它在 VS 2015、GCC >= 6.1 和 Clang >= 3.4 中编译。它在与 XCode 8 (clang-800.0.42.1)、Clang 3.3 和 GCC 5.4 捆绑的 Clang 中失败。每个都给出了一些变化:

candidate constructor not viable: no known conversion from 'nullptr_t' to 'const Foo' for 2nd argument

【问题讨论】:

  • 你有什么问题?
  • 这个合法代码是否符合 C++11 标准?即是编译失败还是编译成功是编译错误?
  • 所有现代编译器(我不知道clang-800.0.42.1有多现代)编译这个很好,而旧版本失败了,所以我急于假设它最近变得有效?

标签: c++ c++11 language-lawyer


【解决方案1】:

您需要一个从 nullptr_t 转换为 Foo 的转换构造函数:

#include <functional>
#include <utility>

struct Foo {
  Foo(std::function<void ()> f = nullptr) { }
  Foo(std::nullptr_t) {}
};

typedef std::pair< void*, Foo > TestPair;

int main(void) {
  Foo f(nullptr); // always works
  //f = nullptr;  // never works
  TestPair p1(nullptr, static_cast< std::function<void()> >(nullptr)); // works
  TestPair p2(nullptr, nullptr); // fails in some compilers
  return 0;
}

MSVC 有时不需要复制构造函数来进行复制初始化。


Foo f = nullptr;

如果没有正确的转换构造函数,这将需要两个用户定义的转换,用于从 std::nullptr_t 到 Foo 的隐式转换(编译器不会这样做):从 std::nullptr_tstd::function&lt;???&gt;,然后到 Foo。对于其他情况,第一次转换可能在 c++17 下工作,但在这种情况下则不行。


std::pair 的构造函数将 nullptr 转发给 Foo 的构造函数,标准库的实现发生了一些变化。

抱歉耽搁了。我在令人敬畏的错误信息和 GNU 标准库的源代码中进行了一次小冒险。

下面是g++-6和g++-5中std::pair的转发构造函数:

// source code from /usr/include/c++/6/bits/stl_pari.h
template<typename _U1, 
         typename _U2, 
         typename
  enable_if<
    _MoveConstructiblePair<_T1, _T2, _U1, _U2>() &&
      _ImplicitlyMoveConvertiblePair<_T1, _T2, _U1, _U2>(),
    bool
  >::type=true>
constexpr pair(_U1&& __x, _U2&& __y)
    : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }

template<typename _U1,
         typename _U2,
         typename
  enable_if<
    _MoveConstructiblePair<_T1, _T2, _U1, _U2>() &&
      !_ImplicitlyMoveConvertiblePair<_T1, _T2, _U1, _U2>(),
    bool
  >::type=false>
explicit constexpr pair(_U1&& __x, _U2&& __y)
    : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }

// source code from /usr/include/c++/5/bits/stl_pari.h
template<class _U1,
         class _U2,
         class = typename
  enable_if<
    __and_<is_convertible<_U1, _T1>,is_convertible<_U2, _T2>>::value
  >::type>
constexpr pair(_U1&& __x, _U2&& __y)
    : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }

// error message from g++ 5.4
// This constructor failed us. 
In file included from /usr/include/c++/5/bits/stl_algobase.h:64:0,
                 from /usr/include/c++/5/memory:62,
                 from main.cc:1:
/usr/include/c++/5/bits/stl_pair.h:144:12: note: candidate: template<class _U1, class _U2, class> constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&)
  constexpr pair(_U1&& __x, _U2&& __y)
            ^
/usr/include/c++/5/bits/stl_pair.h:144:12: note:   template argument deduction/substitution failed:
/usr/include/c++/5/bits/stl_pair.h:141:38: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
       template<class _U1, class _U2, class = typename

g++-5之所以不接受代码是因为std::nullptr_tFoo是不可转换的。

g++-6 的_MoveConstructiblePair 大致可以告诉我们是否可以从另一个对象构造一个对象。在这种情况下,是可构造的。

结论:std::pair 的转发构造函数将他的要求从 Convertible 更改为 MoveConstructible

【讨论】:

  • 我相信你对这两个用户定义的转换是正确的。在 C++14 中,std::pair 构造函数大多更改为添加 constexpr,在 VS 2015 中它们是这样定义的,而在 XCode 8 中则不是。你能解释一下constexpr的区别吗?
  • 不错的答案。 VS 2015 的实现也有等效的检查(使用is_constructible)。 VS 和 GCC 似乎都根据是否可转换分为两个声明,但随后实现两个版本相同。
猜你喜欢
  • 1970-01-01
  • 2016-08-08
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 2012-06-25
  • 2022-01-23
相关资源
最近更新 更多