考虑:
int convert_it(std::string& x)
{
return 5;
}
void takes_int_ref(int& i)
{
}
我想编写一个只有在convert_it
可以应用且结果传递到takes_int_ref
时才存在的函数。也就是说,函数体是:
template <typename A>
void doit(A& a)
{
int i = convert_it(a);
takes_int_ref(i);
}
但是,如果我这样做:
template <typename A>
auto doit(A& a) -> decltype(takes_int_ref(convert_it(a)), void())
它不起作用,因为invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
。
我想到了以下解决方案:
template <typename T>
T& gimme_ref(T t) { throw std::runtime_error("No"); return t; }
template <typename A>
auto doit(A& a) -> decltype(takes_int_ref(gimme_ref(convert_it(a))), void())
然而,它似乎是hackish而且decltype
不再反映函数体的作用。本质上问题似乎是decltype
只接受表达式,而函数体中需要两个语句。
在这里采取什么样的正确方法?
答案 0 :(得分:9)
使用std::declval
:
template <typename A>
auto doit(A& a) -> decltype(
takes_int_ref(std::declval<
decltype(convert_it(std::declval<A&>()))
&>()), void())
{ .. }
std::declval<A&>()
为您提供A&
类型的表达式。 convert_it(A&)
将有效或无效 - 如果它无效,您就会失败。如果它有效,请说它有T
类型。然后,您尝试使用takes_int_ref
致电T&
,以查看 是否有效。如果是,您将转到void
。如果不是,替换失败。
答案 1 :(得分:1)
为了记录,在看到std::declval
解决方案之后,我对什么是hackish的看法发生了变化,我最终接受了这个:
template <typename T> T& lref_of(T&&);
template <typename A>
auto doit(A& a) -> decltype(takes_int_ref(lref_of(convert_it(a))), void())
{ .. }
由于我是从表达式开始而不是类型,因此lref_of(convert_it(a))
比std::declval<decltype(convert_it(a))&>()
更清晰。另外,如果在任何代码中使用它而不是定义lref_of
,则不会定义编译时错误,这比在运行时简单地抛出异常更好。