当它是左值时如何存储引用,当它是右值时如何存储副本
How to store a reference when it is an lvalue, store a copy when it is an rvalue
我想做一个可以用固定数量的参数调用的工厂函数模板,每个参数类型一个模板参数。有两个参数:
template< typename T1, typename T2 >
and_implementation< T1, T2 > and( T1 && p1, T2 && p2 ){
return and_implementation< T1, T2 >( p1, p2 );
}
在 and_implementation
对象中,我想存储对作为左值的每个参数的引用,以及作为右值的每个参数的副本。我不想使用堆。
目标是当我写
auto p1 = ....
auto p2 = ....
auto p3 = and( p1, p3 );
p3
对象仅包含对 p1
和 p2
的引用,但是当我写类似
的内容时
auto p1 = ....
auto p2 = ....
auto p3 = ....
auto p4 = and( p1, and( p2, p3 ));
p4
对象包含对 p1
的引用,但包含 and(p2, p3)
.
的副本
有办法吗?
我想到的(工厂叫invert,只有一个参数)是
template< typename T >
struct invert_impl: public gpio {
T pin;
template< typename TT > invert_impl( TT && p ):
pin( p ) {} // this is line 60
};
template< typename P >
invert_impl< P > invert( P && pin ){
return invert_impl< P >( pin );
}
这适用于
autp pin4 = lpc_gpio< 4 >{};
auto led = invert( pin4 );
但是
autp pin4 = lpc_gpio< 4 >{};
auto led = invert( invert( pin4 ));
我得到(GCC 4.9.3):
main.cpp:60:14: error: invalid initialization of reference of type 'lpc_gpio<4>&' from expression of type 'invert_impl<lpc_gpio<4>&>'
你想多了。您的构造函数不需要是模板,因为在每个具体的模板实例化中,您已经知道构造函数应该接受的确切类型:它应该接受 T
.
template <typename T>
struct invert_impl : public gpio {
T pin;
invert_impl(T p) : pin(p) {}
};
您的模板构造函数失败的原因是它也被选为复制或移动构造函数(如果它比隐式生成的复制和移动构造函数更匹配),这是行不通的。复制和移动构造函数采用 const invert_impl &
和 invert_impl &&
,不能用于初始化 pin
.
注意:从 p
初始化 pin
可能会在此处进行不必要的复制。 std::forward
可以避免这种情况,即使这并不是它最初的目的。
invert_impl(T p) : pin(std::forward<T>(p)) {}
@Yakk 正确地指出,即便如此,仍然存在一些不必要的操作,可以通过让构造函数采用 T&&
并从 invert
转发来避免它们,如下所示:
template <typename T>
struct invert_impl : public gpio {
T pin;
invert_impl(T &&p) : pin(std::forward<T>(p)) {}
};
template <typename T>
invert_impl<T> invert(T &&pin) {
return invert_impl<T>(std::forward<T>(pin));
}
就因为它圆滑:
template<template<class...>class Z, class...Ts>
Z<Ts...> make( Ts&&... ts ) {
return {std::forward<Ts>(ts)...};
}
是一个可以像 make<invert_impl>( pin )
这样调用的函数,它推导出 invert_impl
的类型参数。现在,缺点是它的名声不好。所以我们可以使用一个函数对象:
template<template<class...>class Z,class=void> // =void for SFINAE
struct make {
template<class...Ts>
Z<Ts...> operator()(Ts&&...ts)const{
return {std::forward<Ts>(ts)...};
}
};
现在我们可以做:
static make<invert_impl> invert;
和 invert(blah)
做正确的事 (tm),
也是
static make<and_implementation> _and_;
无需重写粘合代码。 (注意:名为 and
的变量或函数实际上是非法的,因为在 C++ 下 and
是 &&
的别名——所以我将我的命名为 _and_
)。
现在,当我们添加命名运算符 (bwahaha) 时,这会变得更有趣。
第一个十行库:
namespace named_operator {
template<class D>struct make_operator{};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
那么我认为是一个实现:
template<template<class...>class,class...Ts>
std::false_type is_unary(Ts...) { return {}; }
template<template<class>class> std::true_type is_unary() { return {}; }
template<template<class...>class Z>
using unary = decltype( is_unary<Z>() );
template<template<class...>class Z>
struct make<
Z,std::enable_if_t<!unary<Z>{}>
>:named_operator::make_operator<make<Z>> {
template<class...Ts>
Z<Ts...> operator()(Ts&&...ts)const{
return {std::forward<Ts>(ts)...};
}
template<class Lhs, class Rhs>
friend Z<Lhs, Rhs> invoke( Lhs&& lhs, make<Z> m, Rhs&& rhs ) {
return m(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs));
}
};
这给了我们
auto r = p1 *_and_* p2;
作为
的替代品
auto r = _and_(p1, p2);
这很有趣。 (假设我点了所有的 is 并穿过了上面的所有 ts)
我想做一个可以用固定数量的参数调用的工厂函数模板,每个参数类型一个模板参数。有两个参数:
template< typename T1, typename T2 >
and_implementation< T1, T2 > and( T1 && p1, T2 && p2 ){
return and_implementation< T1, T2 >( p1, p2 );
}
在 and_implementation
对象中,我想存储对作为左值的每个参数的引用,以及作为右值的每个参数的副本。我不想使用堆。
目标是当我写
auto p1 = ....
auto p2 = ....
auto p3 = and( p1, p3 );
p3
对象仅包含对 p1
和 p2
的引用,但是当我写类似
auto p1 = ....
auto p2 = ....
auto p3 = ....
auto p4 = and( p1, and( p2, p3 ));
p4
对象包含对 p1
的引用,但包含 and(p2, p3)
.
有办法吗?
我想到的(工厂叫invert,只有一个参数)是
template< typename T >
struct invert_impl: public gpio {
T pin;
template< typename TT > invert_impl( TT && p ):
pin( p ) {} // this is line 60
};
template< typename P >
invert_impl< P > invert( P && pin ){
return invert_impl< P >( pin );
}
这适用于
autp pin4 = lpc_gpio< 4 >{};
auto led = invert( pin4 );
但是
autp pin4 = lpc_gpio< 4 >{};
auto led = invert( invert( pin4 ));
我得到(GCC 4.9.3):
main.cpp:60:14: error: invalid initialization of reference of type 'lpc_gpio<4>&' from expression of type 'invert_impl<lpc_gpio<4>&>'
你想多了。您的构造函数不需要是模板,因为在每个具体的模板实例化中,您已经知道构造函数应该接受的确切类型:它应该接受 T
.
template <typename T>
struct invert_impl : public gpio {
T pin;
invert_impl(T p) : pin(p) {}
};
您的模板构造函数失败的原因是它也被选为复制或移动构造函数(如果它比隐式生成的复制和移动构造函数更匹配),这是行不通的。复制和移动构造函数采用 const invert_impl &
和 invert_impl &&
,不能用于初始化 pin
.
注意:从 p
初始化 pin
可能会在此处进行不必要的复制。 std::forward
可以避免这种情况,即使这并不是它最初的目的。
invert_impl(T p) : pin(std::forward<T>(p)) {}
@Yakk 正确地指出,即便如此,仍然存在一些不必要的操作,可以通过让构造函数采用 T&&
并从 invert
转发来避免它们,如下所示:
template <typename T>
struct invert_impl : public gpio {
T pin;
invert_impl(T &&p) : pin(std::forward<T>(p)) {}
};
template <typename T>
invert_impl<T> invert(T &&pin) {
return invert_impl<T>(std::forward<T>(pin));
}
就因为它圆滑:
template<template<class...>class Z, class...Ts>
Z<Ts...> make( Ts&&... ts ) {
return {std::forward<Ts>(ts)...};
}
是一个可以像 make<invert_impl>( pin )
这样调用的函数,它推导出 invert_impl
的类型参数。现在,缺点是它的名声不好。所以我们可以使用一个函数对象:
template<template<class...>class Z,class=void> // =void for SFINAE
struct make {
template<class...Ts>
Z<Ts...> operator()(Ts&&...ts)const{
return {std::forward<Ts>(ts)...};
}
};
现在我们可以做:
static make<invert_impl> invert;
和 invert(blah)
做正确的事 (tm),
static make<and_implementation> _and_;
无需重写粘合代码。 (注意:名为 and
的变量或函数实际上是非法的,因为在 C++ 下 and
是 &&
的别名——所以我将我的命名为 _and_
)。
现在,当我们添加命名运算符 (bwahaha) 时,这会变得更有趣。
第一个十行库:
namespace named_operator {
template<class D>struct make_operator{};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
那么我认为是一个实现:
template<template<class...>class,class...Ts>
std::false_type is_unary(Ts...) { return {}; }
template<template<class>class> std::true_type is_unary() { return {}; }
template<template<class...>class Z>
using unary = decltype( is_unary<Z>() );
template<template<class...>class Z>
struct make<
Z,std::enable_if_t<!unary<Z>{}>
>:named_operator::make_operator<make<Z>> {
template<class...Ts>
Z<Ts...> operator()(Ts&&...ts)const{
return {std::forward<Ts>(ts)...};
}
template<class Lhs, class Rhs>
friend Z<Lhs, Rhs> invoke( Lhs&& lhs, make<Z> m, Rhs&& rhs ) {
return m(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs));
}
};
这给了我们
auto r = p1 *_and_* p2;
作为
的替代品auto r = _and_(p1, p2);
这很有趣。 (假设我点了所有的 is 并穿过了上面的所有 ts)