如何使用需要非const引用的函数编写decltype表达式?

时间:2015-06-11 15:43:29

标签: c++ templates c++11 decltype

考虑:

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只接受表达式,而函数体中需要两个语句。

在这里采取什么样的正确方法?

2 个答案:

答案 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,则不会定义编译时错误,这比在运行时简单地抛出异常更好。