如何省略推导出的参数类型的完美转发?

时间:2015-08-12 17:12:05

标签: c++ c++11 move-semantics perfect-forwarding template-deduction

让我说我有一些函数是我想要推导出的类型的参数类型(或几种参数类型)。我也希望基于rvalue或lvalue这一事实的不同行为。直接写出它会导致一个明显的(对于有经验的人)陷阱因为完美的转发:

#include <iostream>
#include <vector>

template <typename T>
void f (T &&v) // thought to be rvalue version
{
   // some behavior based on the fact that v is rvalue
   auto p = std::move (v);
   (void) p;
}

template <typename T>
void f (const T &v) // never called
{  
   auto p = v;
   (void) p;
}

int main ()
{
    std::vector<int> x = {252, 135};
    auto &z = x;
    f (z);
    std::cout << x.size () << '\n'; // woah, unexpected 0 or crash
}

即使这种行为的偷偷摸摸的性质已经是一个有趣的观点,但我的问题实际上是不同的 - 对于这种情况,什么是好的,简洁的,可理解的解决方法?

如果没有推断出完美转发的类型(例如,它已经是外部类的已知模板参数或类似的东西)那么使用typename identity<T>::type&&代替{{1}的众所周知的解决方法但由于相同的结构是避免类型推导的一种解决方法,因此在这种情况下它没有帮助。我可以想象一些解决它的技巧,但代码清晰度可能会被破坏,它看起来与类似的非模板函数完全不同。

3 个答案:

答案 0 :(得分:8)

选项#1

隐藏在模板参数列表中的SFINAE:

#include <type_traits>

template <typename T
        , typename = typename std::enable_if<!std::is_lvalue_reference<T>{}>::type>
void f(T&& v)
{

}

template <typename T>
void f(const T& v)
{  

}

DEMO 1

选项#2

SFINAE隐藏在返回类型语法中:

template <typename T>
auto f(T&& v)
    -> typename std::enable_if<!std::is_lvalue_reference<T>{}>::type    
{

}

template <typename T>
void f(const T& v)
{  

}

DEMO 2

在C ++ 14 typename std::enable_if<!std::is_lvalue_reference<T>{}>::type中可以缩短为:

std::enable_if_t<!std::is_lvalue_reference<T>{}> 

无论如何,即使在C ++ 11中,如果您发现它更简洁,也可以使用别名模板缩短语法:

template <typename T>
using check_rvalue = typename std::enable_if<!std::is_lvalue_reference<T>{}>::type;

DEMO 3

答案 1 :(得分:4)

第二级实施如何:

#include <utility>
#include <type_traits>

// For when f is called with an rvalue.
template <typename T>
void f_impl(T && t, std::false_type) { /* ... */ }

// For when f is called with an lvalue.
template <typename T>
void f_impl(T & t, std::true_type) { /* ... */ }

template <typename T>
void f(T && t)
{
    f_impl(std::forward<T>(t), std::is_reference<T>());
}

答案 2 :(得分:2)

我认为SFINAE应该提供帮助:

template<typename T,
         typename = typename std::enable_if<!std::is_lvalue_reference<T>::value>::type>
void f (T &&v) // thought to be rvalue version
{
   // some behavior based on the fact that v is rvalue
   auto p = std::move (v);
   (void) p;
}

template <typename T>
void f (const T &v) // never called
{  
   auto p = v;
   (void) p;
}