关注tuple continuation monad,假设我定义了一个仿函数std_tuple
,从monad-tuple的导管到std::tuple
:
auto std_tuple = [](auto... args)
{
return [=](auto f){ return f(std::make_tuple(args...)); };
};
现在我们可以在期望std::tuple
的上下文中使用monad-tuples:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main()
{
tuple(1,2,3)(std_tuple)(f);
}
到目前为止一切顺利。除了这不编译。 Clang 3.4.1抱怨:
注意:候选模板被忽略:无法推断模板参数'$ auto-1-0'
在f(t)
仿函数内的std_tuple
来电。
这是正确的,是不是那些模板argumments可以推断?如果是肯定的,为什么?
答案 0 :(得分:4)
重现问题的简单案例:
void f(int) {}
void f(double) {}
template<class T> void call_with_3( T t ) { t(3); }
int main() {
call_with_3( f );
}
在这里,我们可以看到在我们将f
传递给call_with_3
时无法确定哪个f
。现在,你似乎没有多次重载(你只有一个template
!),但......
template
不是实例。 template
函数是函数的工厂,而不是函数。
没有任何对象或价值可以传播。
当您将函数名称作为参数传递时,重载决策将启动。如果目标类型已知(作为函数引用或指针),则用于对函数名称执行重载解析。
在这种情况下,您将函数名称传递给auto
(static struct f_overload_set_t {
template<class... Args>
auto operator()(Args&&... args) const {
return f(std::forward<Args>(args)...);
}
} f_overload_set;
参数),因此没有可以完成的重载决策,因此找不到特定的值,所以你得到一个错误。
您可以创建一个对象,其效果是对具有给定函数名称的调用参数执行重载解析。我称之为过载集对象。
->decltype( f( std::declval<Args>()... ) )
在C ++ 11中,const
之后需要f_overload_set(blah)
。
现在f(blah)
将会(几乎)执行f_overload_set
时发生的事情#define OVERLOAD_SET( FUNC )\
([](auto&&... args){\
return FUNC(std::forward<decltype(args)>(args)...);\
})
,但FUNC
是实际对象。所以你可以传递它。
生成此类重载集的宏相对容易编写。他们也可以使用lambdas,因为如果你想的话,上面就像一个无状态的lambda。
基于无状态lambda的宏重载集生成器的优点在于它可以在使用点创建。来自@ dyp上面的评论:
operator[]
(注意:[[
周围没有括号,因为它阻止了ADL)。围绕其他一切的括号,因为否则如果在下标操作(template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main() {
tuple(1,2,3)(std_tuple)(OVERLOAD_SET(f));
}
)中使用,它将被解析为{{1}}启动属性,以及其他点(感谢@ecatmur))
使您的代码成为:
{{1}}