假设我的代码中的某个地方是一个带有通用引用参数的函数foo
,我无法更改:
template<typename T>
auto foo(T&& t) { std::cout<<"general version"<<std::endl; }
现在我想为给定的类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; }
Demo。但是,对于更多参数,这会非常严重地缩放 。
或者,我可以通过值传递,这似乎也捕获了以前的所有情况:
auto foo(A a) { std::cout<<"A"<<std::endl; }
Demo。然而,现在需要复制大对象( - 至少原则上)。
围绕这些问题有优雅的方法吗?
请记住,我无法更改通用参考功能,因此不可能使用SFINAE等。
答案 0 :(得分:7)
SFINAE?
template <typename T> auto foo(T&& );
template <typename T,
typename = only_if_is<T, A>>
auto foo(T&& );
foo(A{}); // error: ambiguous
编写一个采用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
您可以做的最好的事情是使用选择器引入转发功能:
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>{});
}
但是你提到in a comment这对你也没有用。所以我想,你的一个选择是:向标准委员会写一份提案!
Acutally,有希望!如果概念被采纳(好的,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{})
}