复制父节点

Copying parent nodes

我正在尝试编写一个只允许从父节点复制的复制构造函数。由于我不得不使用 C++17,我想通过使用 std::enable_if 类型特征模仿 concepts/require 子句来实现这一点(如果有更好的方法来实现这一点,请告诉我)。

#include <functional>
#include <iostream>

template<int N>
struct node {
    node() {
        std::cout << "def-ctor  N=" << N << "\n";
    }

    node(node const&) {
        std::cout << "cpy-ctor  N=" << N << "\n";
    }

    template<int O
        // , typename = std::enable_if_t<(N > O)>
    >
    node(node<O> const&) {
        std::cout << "cpy-ctor  N=" << N << " O=" << O << "  ";
        // static_assert(N > O);             // fails due to 3 > 4
        if constexpr (N > O)  std::cout << "N > O\n";
        if constexpr (N == O) std::cout << "N == O\n";
        if constexpr (N < O)  std::cout << "N < O\n";
    }
};

node<3> n3;
auto n4 = node<4>{n3};

//

template<int N>
using f = std::function<void(node<N> const&)>;

auto f3 = f<3>{[](auto const&){}};
auto f4 = f<4>{f3};

//

auto main() -> int {}

上面的代码打印了预期的输出:

cpy-ctor N=4 O=3 N > O

但是(令我惊讶的是)当取消注释静态断言时,编译失败说明 3 肯定不能大于 4。我想我真的不能反驳。

我怀疑这里正在发生某种复制省略恶作剧。为什么在引擎盖下需要反转副本,我无法理解。

所以我的问题是:这是怎么回事?为什么我不能将这个复制构造函数限制为只接受父节点?我该如何补救?

Live example.

你的 node(node<O> const&) 代码和 SFINAE 用法(目前评论)对我来说没问题。

如果我取消注释 static_assert,我的本地 g++ 11.2.0 by msys2 会产生以下错误:

a.cpp: In instantiation of 'node<N>::node(const node<O>&) [with int O = 4; int N = 3]':
C:/tools/msys64/mingw64/include/c++/11.2.0/bits/invoke.h:61:36:   required from 'constexpr _Res std::__invoke_impl(std::__invoke_other, _Fn&&, _Args&& ...) [with _Res = void; _Fn = std::function<void(const node<3>&)>&; _Args = {const node<4>&}]'
C:/tools/msys64/mingw64/include/c++/11.2.0/bits/invoke.h:111:28:   required from 'constexpr std::enable_if_t<is_invocable_r_v<_Res, _Callable, _Args ...>, _Res> std::__invoke_r(_Callable&&, _Args&& ...) [with _Res = void; _Callable = std::function<void(const node<3>&)>&; _Args = {const node<4>&}; std::enable_if_t<is_invocable_r_v<_Res, _Callable, _Args ...>, _Res> = void]'
C:/tools/msys64/mingw64/include/c++/11.2.0/bits/std_function.h:291:30:   required from 'static _Res std::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_invoke(const std::_Any_data&, _ArgTypes&& ...) [with _Res = void; _Functor = std::function<void(const node<3>&)>; _ArgTypes = {const node<4>&}]'
C:/tools/msys64/mingw64/include/c++/11.2.0/bits/std_function.h:422:21:   required from 'std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = std::function<void(const node<3>&)>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = void; _ArgTypes = {const node<4>&}]'
a.cpp:35:18:   required from here
a.cpp:19:25: error: static assertion failed
   19 |         static_assert(N > O);             // fails due to 3 > 4
      |                       ~~^~~
a.cpp:19:25: note: '(3 > 4)' evaluates to false

让我们打开包装。

  1. 它显然想从现有的node<4>某处构造一个node<3>
  2. auto f4 = f<4>{f3};
  3. 需要某处
  4. 此行从现有的 std::function<void(node<3> const&)>
  5. 构建 std::function<void(node<4> const&)>
  6. 错误发生在名为 __invoke_impl
  7. 的辅助函数中

如何将采用 node<3> 的函数转换为采用 node<4> 的函数?唯一合理的方法是:

void f4(node<4> const &arg) {
    return f3(arg);  // f3(node<3> const &)
}

实际上,__invoke_impl 或多或少就是这样做的。

注意这里需要从node<4>node<3>的隐式转换。因此,程序必须从 node<4> 对象构造一个 node<3> 对象。但是 static_assert 禁止这样做。

您可以通过将 static_assert 替换为调试输出或调试器断点并查看它实际调用的时间来仔细检查。