【问题标题】:template specialization for rvalue and lvalue reference右值和左值引用的模板特化
【发布时间】:2014-02-14 09:53:43
【问题描述】:

我玩了一下转发,得到了下面的例子,它工作得很好。

void Func2( int& a, int& b) { cout << "V1" << endl; }
void Func2( int&& a, int& b) { cout << "V2" << endl; }
void Func2( int& a, int&& b) { cout << "V3" << endl; }
void Func2( int&& a, int&& b) { cout << "V4" << endl; }

    template < typename T, typename U>
void Func( T&& t, U&& u) 
{
    X::Func3( std::forward<T>(t), std::forward<U>(u));
    Func2( std::forward<T>(t), std::forward<U>(u));
}

int main()
{
    int a, b;
    Func( a, b);
    Func( 1, b);
    Func( a, 2);
    Func( 1, 2);

    return 0;
}

但我还希望有一个Func2 的函数模板,以用任何类型替换int 类型,或者如果不可能,则替换为具有专用方法的类。以下代码片段将无法编译:

class X
{
    public: 
        template < typename T, typename U>
            static void Func3( T& t, U& u) { cout << "X1" << endl; }

        template < typename T, typename U>
            static void Func3( T&& t, U& u) { cout << "X2" << endl; }

        template < typename T, typename U>
            static void Func3( T& t, U&& u) { cout << "X3" << endl; }

        template < typename T, typename U>
            static void Func3( T&& t, U&& u) { cout << "X4" << endl; }
}; 

结果:

main.cpp: In instantiation of 'void Func(T&&, U&&) [with T = int&; U = int&]':
main.cpp:36:18:   required from here
main.cpp:29:57: error: call of overloaded 'Func3(int&, int&)' is ambiguous
         X::Func3( std::forward<T>(t), std::forward<U>(u));
                                                         ^
main.cpp:29:57: note: candidates are:
main.cpp:9:29: note: static void X::Func3(T&, U&) [with T = int; U = int]
                 static void Func3( T& t, U& u) { cout << "X1" << endl; }
                             ^
main.cpp:12:29: note: static void X::Func3(T&&, U&) [with T = int&; U = int]
                 static void Func3( T&& t, U& u) { cout << "X2" << endl; }
                             ^
main.cpp:15:29: note: static void X::Func3(T&, U&&) [with T = int; U = int&]
                 static void Func3( T& t, U&& u) { cout << "X3" << endl; }
                             ^
main.cpp:18:29: note: static void X::Func3(T&&, U&&) [with T = int&; U = int&]
                 static void Func3( T&& t, U&& u) { cout << "X4" << endl; }
                             ^

【问题讨论】:

  • 请添加编译器错误信息。

标签: c++ templates c++11 overloading rvalue-reference


【解决方案1】:

正如其他答案所说,这些调用是模棱两可的,因为通用引用 T&amp;&amp;, U&amp;&amp; 匹配左值引用和右值引用。您可以使用std::enable_if 手动删除歧义,例如

template <bool C>
using only_if = typename std::enable_if <C>::type;

template <typename T>
using is_lref = std::is_lvalue_reference <T>;

struct X
{
    template <typename T, typename U>
    static void
    Func3(T& t, U& u) { cout << "X1" << endl; }

    template <typename T, typename U>
    static only_if <!is_lref <T>()>
    Func3(T&& t, U& u) { cout << "X2" << endl; }

    template <typename T, typename U>
    static only_if <!is_lref <U>()>
    Func3(T& t, U&& u) { cout << "X3" << endl; }

    template <typename T, typename U>
    static only_if <!(is_lref <T>() || is_lref <U>())>
    Func3(T&& t, U&& u) { cout << "X4" << endl; }
};

另见live example。这样你就明确地说 T&amp;&amp; 不应该匹配左值引用。

这种方法很难推广到更多的输入参数。在这种情况下,考虑一次只处理一个参数,将剩余的参数保留为右值引用。因此,您只需要两个重载和递归调用,但具体形式取决于您想要做什么。

【讨论】:

  • only_if 一般命名为enable_if_t(如果我们保留默认参数)。
  • !(is_lref &lt;T&gt;() || is_lref &lt;U&gt;()) 可以重写 !is_lref &lt;T&gt;() &amp;&amp; !is_lref&lt;U&gt;() 以避免否定括号。
  • @Jarod42 谢谢,我知道enable_if_t 应该可以工作,我只尝试过一次,但没有工作,所以我只是匆忙做了自己的定义。
  • 注意:给定const int c = 42;X::Func3(c, c) 有效,而Func2(c, c) 无效。
  • @Jarod42 是的。我没有在问题中明确看到这个要求;只有X 不会编译。因此,如果不需要const,可以使用std::is_const 添加检查,或者如果将X 设置为合适的模板,请查看您的答案。
【解决方案2】:

以下可能会有所帮助:

template <typename T, typename U>
class X
{
public:
    static void Func3(T& t, U& u) { cout << "X1" << endl; }
    static void Func3(T&& t, U& u) { std::cout << "X2" << std::endl; }
    static void Func3(T& t, U&& u) { cout << "X3" << endl; }
    static void Func3(T&& t, U&& u) { cout << "X4" << endl; }
};

template <typename T, typename U>
void Func(T&& t, U&& u)
{
    X<typename std::decay<T>::type, typename std::decay<U>::type>::Func3( std::forward<T>(t), std::forward<U>(u));
    Func2( std::forward<T>(t), std::forward<U>(u));
}

所以 Func3 使用真正的 r 值引用而不是通用引用。

【讨论】:

  • 模板类中的静态非模板函数是一个不错的技巧,比 SO 上建议的通常 enable_if 更好。但我认为在实际情况下它失败了,我们想要分解 Func3 代码,即模板的最初目的。
  • 这确实很好,如果整体设计允许将X作为模板。但是,为什么std::decay 而不仅仅是std::remove_reference?在这种情况下输入 const T&amp; 会起作用吗?
  • @iavr:相当于Func2Func2const int&amp; 不匹配。使用std::remove_reference,我会匹配它。
【解决方案3】:

这是因为 T&& 在模板函数中非常特殊,它不是右值引用。它被命名为通用引用并绑定到参数的类型。因此,Func3 的多个候选者会产生相同的绑定,并且您会以一个模棱两可的调用结束。

下面的代码显示“真假真”并显示通用引用的行为。

#include <iostream>
#include <type_traits>

template <typename T> 
bool foo( T&& v ) {
    return std::is_rvalue_reference<decltype(v)>::value;
}

int main() {
    std::cout << std::boolalpha;
    std::cout << foo( 1 ) << std::endl;
    int a{};
    std::cout << foo( a ) << std::endl;
    std::cout << foo( std::move(a) ) << std::endl;
}

仅 const T& 和 T&& 的重载不是一个好的解决方案,因为 T&& 将比 const T& 更好地匹配。这与非模板函数不同。

#include <iostream>
#include <type_traits>

template <typename T> 
bool foo( T&& v ) {
    return false;
}

template <typename T> 
bool foo( T const & v ) {
    return true;
}

bool bar( int&& v ) {
    return false;
}

bool bar( int const & v ) {
    return true;
}

int main() {
    std::cout << std::boolalpha;
    int a{};
    int const b {};
    std::cout << foo( a ) << std::endl; // false
    std::cout << foo( b ) << std::endl; // true

    std::cout << bar( a ) << std::endl; // true
    std::cout << bar( b ) << std::endl; // true
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2015-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多