我想制作一个工厂函数模板,可以使用固定数量的参数调用,每个参数类型都是模板参数。有两个参数:
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)
的副本。
有办法做到这一点吗?
我想出的是(工厂被称为反转并且只有一个参数)
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>&>'
答案 0 :(得分:1)
你过分思考问题。您的构造函数不需要是模板,因为在每个具体的模板实例化中,您已经知道构造函数应该接受的确切类型:它应该接受T
。
template <typename T>
struct invert_impl : public gpio {
T pin;
invert_impl(T p) : pin(p) {}
};
模板构造函数失败的原因是因为它也被选为复制或移动构造函数(如果它比隐式生成的复制和移动构造函数更好匹配),这是无效的。复制和移动构造函数采用const invert_impl &
和invert_impl &&
,不能用于初始化pin
。
注意:从pin
初始化p
可能会在此处制作不必要的副本。 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));
}
答案 1 :(得分:0)
仅仅因为它很光滑:
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);
这很有趣。 (假设我点了所有的并且越过了上面的所有ts)