C++使用通用引用函数模板的重载分辨率,无法更改

dav*_*igh 6 c++ templates overload-resolution c++11 universal-reference

假设我的代码中的某个地方是一个foo带有通用引用参数的函数,我无法更改:

template<typename T>
auto foo(T&& t) { std::cout<<"general version"<<std::endl; }
Run Code Online (Sandbox Code Playgroud)

现在我想foo为给定的类重载A,并确保A调用任何限定符和引用类型的重载.为此,我可以蛮力地为所有可能的资格提供超载(暂时忽略volatile):

auto foo(A & a) { std::cout<<"A&"<<std::endl; }
auto foo(A const& a) { std::cout<<"A const&"<<std::endl; }
auto foo(A && a) { std::cout<<"A &&"<<std::endl; }
auto foo(A const&& a) { std::cout<<"A const&&"<<std::endl; }
Run Code Online (Sandbox Code Playgroud)

演示.然而,这对于更多参数来说非常严重.

或者,我可以传递值,这似乎也捕获了以前的所有情况:

auto foo(A a) { std::cout<<"A"<<std::endl; }
Run Code Online (Sandbox Code Playgroud)

演示.然而,现在需要复制大对象( - 至少原则上).

这些问题有一种优雅的方式吗?

请记住,我无法更改通用参考功能,因此SFINAE等不可能.

Bar*_*rry 7

老实说,我觉得你在这里运气不好.典型的方法都失败了.你可以做...

SFINAE?

template <typename T> auto foo(T&& );
template <typename T,
          typename = only_if_is<T, A>>
auto foo(T&& );

foo(A{}); // error: ambiguous
Run Code Online (Sandbox Code Playgroud)

写一个采用l或rvalue引用的类?

template <typename T> lref_or_ref { ... };

template <typename T> auto foo(T&& );
auto foo(lref_or_ref<A> );

foo(A{}); // calls general, it's a better match
Run Code Online (Sandbox Code Playgroud)

您可以做的最好的事情是使用选择器引入转发功能:

template <int I> struct chooser : chooser<I - 1> { };
template <> struct chooser<0> { };

template <typename T>
auto bar(T&& t, chooser<0> ) {
    // worst-option, general case
    foo(std::forward<T>(t));
}

template <typename T,
          typename = only_if_is<T, A>>
auto bar(T&& t, chooser<1>) {
    // A-specific
}

template <typename T>
auto bar(T&& t) {
    bar(std::forward<T>(t), chooser<20>{});
}
Run Code Online (Sandbox Code Playgroud)

但是你在评论中提到这对你也不适用.所以我想,你的一个选择是:向标准委员会写一份提案!


实际上,有希望!如果概念得到采纳(好点,TartanLlama!):

template <typename T>
    requires IsForwardRefTo<T, A>
auto foo(T&& t) {
    // since this is more constrained than the generic forwarding reference
    // this one should be preferred for foo(A{})
}
Run Code Online (Sandbox Code Playgroud)