C++ SFINAE 在引用类型和转换为引用的参数之间选择重载?

C++ SFINAE to choose an overload between reference type and argument converted to reference?

考虑:

struct BigData {...};

// Let's get a BigData by reference, but use it as a value.
// For example, we may want to make a copy of the object, but we'd
// like to avoid the pass-by-value overhead at the call site.
template <typename T, *some template magic?*>
void processData(T& t) {
    printf("Your BigData was converted to a reference argument.\n");
    ...
}

// Now, we want an overload that will know that there wasn't a
// conversion to reference and will treat it like a reference.
// Perhaps we are adding this BigData to a list of references.
template <typename T, *some template magic?*>
void processData(T& t) {
    printf("You gave me a BigData reference.\n");
    ...
}

int main() {
    BigData data;
    BigData& ref = data;

    processData(data); // "Your BigData was converted to a reference argument."
    processData(ref); // "You gave me a BigData reference."

    return 0;
}

简而言之,我的 objective 是要有重载来区分引用绑定的来源 - 类型的值或(已经)引用类型。我尝试将 std::enable_if 和系列与引用和不引用 T 的重载结合使用,但我找不到实现此目的的方法。非常感谢任何帮助!

我认为您不了解引用绑定的工作原理。您不仅可以将一个引用绑定到另一个引用,还可以绑定一个值类型的值(即没有引用限定)

所以如果你想通过引用传递BigData对象,你只需要执行以下操作

template <typename T>
void processData(T& t) {
    cout << "Your BigData was passed as a reference argument" << endl;
    // ...
}

int main() {
    BigData data;
    BigData& ref = data;

    processData(data); 
    processData(ref);

    return 0;
}

这里两个 processData 调用都将通过引用传递 BigData 对象(即在函数调用期间没有副本)。

您不需要处理传递给函数的对象是与正常情况不同的引用的情况。 std::enable_if 这里不需要。

此外,无法区分这两种情况,无论是使用 data 还是 ref 作为函数的参数,因为它们都是左值。如果要检查表达式的 decltype 是否为引用,则必须执行以下操作

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

template <typename T, std::enable_if_t<std::is_reference<T>::value>* = nullptr>
void processData(std::add_lvalue_reference_t<T>) {
    cout << "You gave me a reference argument." << endl;
}

template <typename T, std::enable_if_t<!std::is_reference<T>::value>* = nullptr>
void processData(std::add_lvalue_reference_t<T>) {
    cout << "Your argument was converted to a reference." << endl;
}

int main() {
    auto integer_value = 1;
    const auto& integer_ref = 2;
    processData<decltype(integer_value)>(integer_value);
    processData<decltype(integer_ref)>(integer_ref);

    return 0;
}