C++ 重载解析,具有不可更改的通用参考函数模板

问题描述 投票:0回答:1

假设我的代码中的某处是一个具有通用参考参数的函数

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; }

演示。然而,对于更多参数来说,这会导致非常糟糕

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

auto foo(A a) { std::cout<<"A"<<std::endl; }

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

有没有一种优雅的方法来解决这些问题?

请记住,我无法更改通用参考函数,因此 SFINAE 之类的功能是不可能的。

c++ templates c++11 overload-resolution universal-reference
1个回答
7
投票

C++20 更新:以下答案对于 C++11 到 C++17 仍然适用,但在 C++20 中您可以执行以下操作:

template <typename T>
    requires std::same_as<std::remove_cvref_t<T>, A>
auto foo(T&& t) {
    // since this is more constrained than the generic forwarding reference
    // this one should be preferred for foo(A{})
}

您可以通过创建命名概念来使用更方便的语法:

template <typename T, typename U>
concept DecaysTo = std::same_as<U, std::decay_t<T>>;

// longest form
template <typename T> requires DecaysTo<T, A> void foo(T&&);

// middle form
template <DecaysTo<A> T> void foo(T&&);

// abbreviated form
void foo(DecaysTo<A> auto&&);

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

SFINAE?

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

foo(A{}); // error: ambiguous

编写一个采用左值或右值引用的类?

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>{});
}

但是您在评论中提到这对您也不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.