C++11有没有办法匹配返回值?

5
我正在尝试提供一个调用提供的lambda的函数,并想知道是否有可能在函数的返回类型为void时返回一个默认值。
这是我目前的代码,它返回lambda的返回值,但如果lambda是void,则返回20。
#include <functional>
#include <iostream>

template <typename H>
auto f(H&& h) -> decltype(h(), void())
{
    return h();
}

template <typename H>
auto f(H&& h) -> decltype(h(), int())
{
    h();
    return 20;
}

int main()
{
    int r = f([](){ std::cout << "test1" << std::endl; return 10; }); // error here
    std::cout << "r: " << r << std::endl;
    r = f([](){ std::cout << "test2" << std::endl; });
    std::cout << "r: " << r << std::endl;

    return 0;
}

这会产生错误:
test.cpp:20:68: error: call of overloaded ‘f(main()::<lambda()>)’ is ambiguous

很显然,这是由于C++不能基于返回类型使用多态性所致。但我想知道是否有任何好的C++11技巧(例如更好地使用 decltype 或一些模板魔术),可以使这成为可能? 我在问这个问题,因为据我所见,这里没有真正的歧义...编译器正在推断 void 返回类型,然后说模糊匹配是否要匹配 int void 版本的 f ,这有点傻。
做这件事的一个原因是,如果函数 f 期望返回值,但用户提供的Lambda不包括 return 语句,则编译器会推断出 void 类型,并发生错误。由于在实际情况下,当用户不关心提供一个合理的默认返回值时,我想知道是否有可能让编译器允许用户省略通常不必要的 return 语句以方便使用。
谢谢。我应该提到我正在使用GCC 4.6.3。 答案:基于Xeo建议使用enable_if,我想到了以下方法,似乎可以工作:
template <typename H>
auto f(H&& h) -> typename std::enable_if<std::is_same<decltype(h()), void>::value, int>::type
{
    h();
    return 20;
}

template <typename H>
auto f(H&& h) -> typename std::enable_if<std::is_same<decltype(h()), int>::value, int>::type
{
    return h();
}

谢谢!


你已经在普通函数上测试过f了吗? - CharlesB
是的,在普通函数中也存在歧义。 - Steve
decltype 的第二个参数:我可以问一下它是干嘛用的吗?(真的,因为我不知道!) - David G
2个回答

3
您可以使用std::enable_if。
第一个返回类型应为std::enable_if<!std::is_same<decltype(h()), void>::value, decltype(h())>::type,第二个返回类型应为std::enable_if<std::is_same<decltype(h()), void>::value, int>::type。这样应该可以使您的代码正常工作。我没有测试过,所以不能确定其完全有效。

@Steve:不,这个是正确的。我认为你可能在某个地方打错了字。请参考这个链接 - Xeo
@Xeo:啊,谢谢,我认为JKor解决方案的第一部分中第二个decltype(h())是有问题的,因为它改变了f()的返回值。根据你的示例,我已经让它工作了,我会在问题中发布代码。 - Steve

2
给我一个理由,说明编译器为什么应该能够推断出在函数内部执行哪个重载,基于你所做的事情?在C++中,重载决议只关心每个参数是否匹配一个参数。返回类型和函数内容一点都不重要。

正如STL所说

简而言之:模板是贪婪的!除非你非常确定会发生什么,否则不要重载它们。

而作为参数的通用引用(template<class T> void f(T&&);)是最贪婪的。

你可以像@JKor所说的那样使用SFINAE来解决这个问题,但简化了:

#include <type_traits>

template<class F>
auto f(F f)
  -> decltype(true? f() : void())
{
  f();
}

template <class F>
auto f(F f) -> decltype(int(f()))
{
  f();
  return 42;
}

int main(){
  f([]{});
}

请注意,GCC 4.7存在一个错误,在其中decltype(int(void_returning_function()))不会导致函数SFINAE。Clang 3.1可以正确处理。
f([]{});

工作,以及
f([]{ return 42; });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接