我有一个结构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]
该有一堆右值引用的方法是用SFINAE:
template <class... Args,
std::enable_if_t<(!std::is_lvalue_reference<Args>::value && ...), int> = 0>
void foo(Args&&... args) { ... }
折叠表达式是C ++ 17,编写一个元函数很容易在C ++ 14中获得相同的行为。这确实是您唯一的选择-您想要一个约束函数模板来推导右值引用,但是唯一可用的语法已重载以表示转发引用。我们可以使模板参数不被推导,但是随后您必须提供它们,这似乎根本不是解决方案。
使用概念,这当然更清洁,但是我们并没有真正改变底层的机制:
template <class... Args>
requires (!std::is_lvalue_reference<Args>::value && ...)
void foo(Args&&... args) { ... }
或更好:
template <class T>
concept NonReference = !std::is_lvalue_reference<T>::value;
template <NonReference... Args>
void foo(Args&&... ) { ... }
值得指出的是,这些都不起作用:
template <class... Args> auto foo(const Args&... args) = delete;
template <class... Args> auto foo(Args&... args) = delete;
因为它们仅删除采用所有左值引用的重载,而您要删除采用任何左值引用的重载。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句