复制父节点
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。我想我真的不能反驳。
我怀疑这里正在发生某种复制省略恶作剧。为什么在引擎盖下需要反转副本,我无法理解。
所以我的问题是:这是怎么回事?为什么我不能将这个复制构造函数限制为只接受父节点?我该如何补救?
你的 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
让我们打开包装。
- 它显然想从现有的
node<4>
某处构造一个node<3>
。
- 行
auto f4 = f<4>{f3};
需要某处
- 此行从现有的
std::function<void(node<3> const&)>
构建 std::function<void(node<4> const&)>
- 错误发生在名为
__invoke_impl
的辅助函数中
如何将采用 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
替换为调试输出或调试器断点并查看它实际调用的时间来仔细检查。
我正在尝试编写一个只允许从父节点复制的复制构造函数。由于我不得不使用 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。我想我真的不能反驳。
我怀疑这里正在发生某种复制省略恶作剧。为什么在引擎盖下需要反转副本,我无法理解。
所以我的问题是:这是怎么回事?为什么我不能将这个复制构造函数限制为只接受父节点?我该如何补救?
你的 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
让我们打开包装。
- 它显然想从现有的
node<4>
某处构造一个node<3>
。 - 行
auto f4 = f<4>{f3};
需要某处
- 此行从现有的
std::function<void(node<3> const&)>
构建 - 错误发生在名为
__invoke_impl
的辅助函数中
std::function<void(node<4> const&)>
如何将采用 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
替换为调试输出或调试器断点并查看它实际调用的时间来仔细检查。