c++ - 具有右值引用而不是使用可变参数模板的转发引用

标签 c++ c++14 variadic-templates rvalue-reference forwarding-reference

我有一个结构 X和一个函数 foo它必须接收 X 的右值引用.

起初我只从一个参数开始,它很简单(哦……更简单的时候):

auto foo(X&& arg) -> void {...};

X x;
foo(x);            // compile error [OK]
foo(std::move(x)); // accepted      [OK]
foo(X{});          // accepted      [OK]

但后来我想扩展并接受可变数量的 X参数(仍然只有右值引用)。

但是有个问题。

  • 第一你不能有auto foo(X&&... args)这将是理想的
  • 第二现在你被迫做template <class... Args> auto foo(Args&&... args)但现在你最终得到了转发引用,它会很乐意接受非临时引用:
template <class... Args>
auto foo(Args&&... args) -> void { ... };

X x1, x2;
foo(x1, x2);                       // accepted [NOT OK]
foo(std::move(x1), std::move(x2)); // accepted [OK]
foo(X{}, X{});                     // accepted [OK]

为什么他们使用这种语法和规则来转发引用让我从一开始就感到困惑。这是一个问题。此语法的另一个问题是 T&&X<T>&&是完全不同的野兽。但我们在这里偏离了轨道。

我知道如何用 static_assert 解决这个问题或 SFINAE但是这两种解决方案都让事情变得有点复杂,而且在我看来,如果语言被设计为一次正确的话,就永远不需要了。甚至不要让我开始 std::initializer_list ...我们又偏离轨道了。

所以我的问题是:女巫是否缺少一个简单的解决方案/技巧 Args&&/args被视为右值引用?


就在我结束这个问题时,我想我有一个解决方案。

为左值引用添加已删除的重载:

template <class... Args>
auto foo(const Args&... args) = delete;

template <class... Args>
auto foo(Args&... args) = delete;

简单,优雅,应该可以,让我们测试一下:

X x1, x2;

foo(x1, x2);                       // compile error [OK]
foo(std::move(x1), std::move(x2)); // accepted [OK]
foo(X{}, X{});                     // accepted [OK]

好的,我知道了!

foo(std::move(x1), x2);            // accepted [oh c'mon]

最佳答案

I know how to solve this with static_assert or SFINAE but both of these solutions complicate things a bit [...] is there a simple solution/trick I am missing [...]?

没有复杂的 SFINAE 表达式或 static_assert 有一个很好的方法可以做到这一点秒。它还不需要您猜测哪些参数是 const,哪些不是(毕竟,如果您尝试使用变量的 const 性,它可能会很快将您带到 UB)。此外,您不必包括 <type_traits>为此,如果你关心那个。
它基于 @Justin's answer就快到了。

让我们看看代码。只需使用 decltype和一个测试函数,你只需要一个声明,根本不需要定义:

#include<utility>

template<typename... T>
void test(const T &&...);

template <class... Args>
auto foo(Args&&... args)
-> decltype(test(std::forward<Args>(args)...), void())
{}

struct X {};

int main() {
    X x1, x2;
    //foo(x1, x2);
    foo(std::move(x1), std::move(x2));
    foo(X{}, X{});
}

如果您切换注释,该示例将不再按要求编译。

基本思想与其他答案中讨论的几乎相同:如果这是右值引用,则可以将其分配给 const 右值引用。不管怎样,因为你不想让那些原本不是 const 的参数成为 const,只需将它们一起测试,然后使用原来的参数。

关于c++ - 具有右值引用而不是使用可变参数模板的转发引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45514116/

相关文章:

c++ - 依赖于规则的模板成员变量

c++ - 如何从数组构造元组

c++ - Qt - 2 类中的信号和槽

c++ - 在析构函数中释放导致内存泄漏

c++ - 如何将函数指针的 std::vector 初始化为运算符重载?

C++:使用 boost::hana 将数组元素扩展为函数的参数

c++ - MinGW 编译的程序在 64 位 Windows 上崩溃

c++ - 所有者绘制菜单的自定义背景

c++ - 使用额外参数 boost 变体访问者

arrays - 模板化多维数组