我有一个结构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]