从nullptr_t转换为具有std :: function隐式转换的类型

时间:2017-03-17 14:14:46

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

我遇到了以下情况,这些情况在几个编译器中失败但在其他编译器中有效。我想知道代码是否有效根据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&gt; = 6.1,而Clang&gt; = 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

1 个答案:

答案 0 :(得分:2)

您需要一个转换构造函数,它将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<???>然后再到Foo。对于其他情况,第一次转换可能在c ++ 17下工作,但在这种情况下不行。

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

抱歉延误。我在令人敬畏的错误messsssssage和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